non interactive mp works

This commit is contained in:
Janmajaya Mall
2024-06-18 19:45:04 +05:30
parent 88fdc6ac5c
commit a95751f560
17 changed files with 1230 additions and 496 deletions

View File

@@ -20,6 +20,7 @@ use crate::{
backend::{
ArithmeticOps, GetModulus, ModInit, ModularOpsU64, Modulus, ShoupMatrixFMA, VectorOps,
},
bool::parameters::ParameterVariant,
decomposer::{Decomposer, DefaultDecomposer, NumInfo, RlweDecomposer},
lwe::{decrypt_lwe, encrypt_lwe, lwe_key_switch, lwe_ksk_keygen, measure_noise_lwe, LweSecret},
multi_party::{
@@ -48,10 +49,12 @@ use crate::{
use super::{
keys::{
ClientKey, CommonReferenceSeededCollectivePublicKeyShare,
CommonReferenceSeededMultiPartyServerKeyShare, InteractiveMultiPartyClientKey,
NonInteractiveMultiPartyClientKey, SeededMultiPartyServerKey,
SeededNonInteractiveMultiPartyServerKey, SeededSinglePartyServerKey,
ServerKeyEvaluationDomain, ShoupServerKeyEvaluationDomain, SinglePartyClientKey,
CommonReferenceSeededMultiPartyServerKeyShare,
CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare,
InteractiveMultiPartyClientKey, NonInteractiveMultiPartyClientKey,
SeededMultiPartyServerKey, SeededNonInteractiveMultiPartyServerKey,
SeededSinglePartyServerKey, ServerKeyEvaluationDomain, ShoupServerKeyEvaluationDomain,
SinglePartyClientKey,
},
parameters::{
BoolParameters, CiphertextModulus, DecompositionCount, DecompostionLogBase,
@@ -59,30 +62,6 @@ use super::{
},
};
pub struct CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<M: Matrix, S> {
/// (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>,
auto_keys_share: HashMap<usize, M>,
lwe_ksk_share: M::R,
user_index: usize,
cr_seed: S,
}
impl<M: Matrix, S> CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<M, S> {
fn ui_to_s_ksk_zero_encs_for_user_i(&self, user_i: usize) -> &M {
assert!(user_i != self.user_index);
if user_i < self.user_index {
&self.others_ksk_zero_encs[user_i]
} else {
&self.others_ksk_zero_encs[user_i - 1]
}
}
}
pub struct MultiPartyCrs<S> {
pub(super) seed: S,
}
@@ -405,19 +384,25 @@ where
nor_test_vec: M::R,
xor_test_vec: M::R,
xnor_test_vec: M::R,
/// Non-interactive u_i -> s key switch decomposer
ni_ui_to_s_ks_decomposer: Option<DefaultDecomposer<M::MatElement>>,
_phantom: PhantomData<SKey>,
}
impl<M: Matrix, NttOp, RlweModOp, LweModOp, Skey>
BoolEvaluator<M, NttOp, RlweModOp, LweModOp, Skey>
{
pub(super) fn parameters(&self) -> &BoolParameters<M::MatElement> {
pub(crate) fn parameters(&self) -> &BoolParameters<M::MatElement> {
&self.pbs_info.parameters
}
pub(super) fn pbs_info(&self) -> &BoolPbsInfo<M, NttOp, RlweModOp, LweModOp> {
&self.pbs_info
}
pub(super) fn ni_ui_to_s_ks_decomposer(&self) -> &Option<DefaultDecomposer<M::MatElement>> {
&self.ni_ui_to_s_ks_decomposer
}
}
fn trim_rgsw_ct_matrix_from_rgrg_to_rlrg<
@@ -628,6 +613,15 @@ where
let scratch_memory = ScratchMemory::new(&parameters);
let ni_ui_to_s_ks_decomposer = if parameters.variant()
== &ParameterVariant::NonInteractiveMultiParty
{
Some(parameters
.non_interactive_ui_to_s_key_switch_decomposer::<DefaultDecomposer<M::MatElement>>())
} else {
None
};
let pbs_info = BoolPbsInfo {
auto_decomposer: parameters.auto_decomposer(),
lwe_decomposer: parameters.lwe_decomposer(),
@@ -651,6 +645,7 @@ where
nor_test_vec,
xnor_test_vec,
xor_test_vec,
ni_ui_to_s_ks_decomposer,
_phantom: PhantomData,
}
}
@@ -665,6 +660,8 @@ where
&self,
client_key: &K,
) -> SeededSinglePartyServerKey<M, BoolParameters<M::MatElement>, [u8; 32]> {
assert_eq!(self.parameters().variant(), &ParameterVariant::SingleParty);
DefaultSecureRng::with_local_mut(|rng| {
let mut main_seed = [0u8; 32];
rng.fill_bytes(&mut main_seed);
@@ -773,6 +770,8 @@ where
client_key: &K,
) -> CommonReferenceSeededMultiPartyServerKeyShare<M, BoolParameters<M::MatElement>, [u8; 32]>
{
assert_eq!(self.parameters().variant(), &ParameterVariant::MultiParty);
DefaultSecureRng::with_local_mut(|rng| {
let mut main_prng = DefaultSecureRng::new_seeded(cr_seed);
@@ -908,9 +907,14 @@ where
where
M: Clone + Debug,
{
assert_eq!(
self.parameters().variant(),
&ParameterVariant::NonInteractiveMultiParty
);
// sanity checks
let key_order = {
let existing_key_order = key_shares.iter().map(|s| s.user_index).collect_vec();
let existing_key_order = key_shares.iter().map(|s| s.user_index()).collect_vec();
// record the order s.t. key_order[i] stores the position of i^th
// users key share in existing order
@@ -940,15 +944,15 @@ where
let mut ui_to_s_ksks = key_shares
.iter()
.map(|share| {
let mut useri_ui_to_s_ksk = share.ui_to_s_ksk.clone();
let mut useri_ui_to_s_ksk = share.ui_to_s_ksk().clone();
assert!(
useri_ui_to_s_ksk.dimension() == (ui_to_s_ksk_decomposition_count.0, ring_size)
);
key_shares
.iter()
.filter(|x| x.user_index != share.user_index)
.filter(|x| x.user_index() != share.user_index())
.for_each(|(other_share)| {
let op2 = other_share.ui_to_s_ksk_zero_encs_for_user_i(share.user_index);
let op2 = other_share.ui_to_s_ksk_zero_encs_for_user_i(share.user_index());
assert!(op2.dimension() == (ui_to_s_ksk_decomposition_count.0, ring_size));
izip!(useri_ui_to_s_ksk.iter_rows_mut(), op2.iter_rows()).for_each(
|(add_to, add_from)| {
@@ -982,7 +986,7 @@ where
.map(|share| {
let mut ksk_prng = DefaultSecureRng::new_seeded(
cr_seed
.ui_to_s_ks_seed_for_user_i::<DefaultSecureRng>(share.user_index),
.ui_to_s_ks_seed_for_user_i::<DefaultSecureRng>(share.user_index()),
);
let mut ais = M::zeros(ui_to_s_ksk_decomposition_count.0, ring_size);
@@ -1036,7 +1040,7 @@ where
key_shares.iter().for_each(|s| {
rlwe_modop.elwise_add_mut(
tmp_space.as_mut(),
s.ni_rgsw_cts.1[lwe_index].get_row_slice(d_index),
s.ni_rgsw_cts().1[lwe_index].get_row_slice(d_index),
);
});
@@ -1130,7 +1134,7 @@ where
|(share, user_uitos_ksk_partb_eval, user_uitos_ksk_parta_eval)| {
// RGSW_s(X^{s[i]})
let rgsw_cts_user_i_eval = izip!(
share.ni_rgsw_cts.0.iter(),
share.ni_rgsw_cts().0.iter(),
decomp_ni_rgsw_neg_ais.iter(),
decomp_ni_rgsws_part_1_acc.iter()
)
@@ -1370,7 +1374,8 @@ where
let mut key = M::zeros(self.parameters().auto_decomposition_count().0, ring_size);
key_shares.iter().for_each(|s| {
let auto_key_share_i = s.auto_keys_share.get(&i).expect("Auto key {i} missing");
let auto_key_share_i =
s.auto_keys_share().get(&i).expect("Auto key {i} missing");
assert!(
auto_key_share_i.dimension()
== (self.parameters().auto_decomposition_count().0, ring_size)
@@ -1393,10 +1398,10 @@ where
M::R::zeros(self.parameters().lwe_decomposition_count().0 * ring_size);
key_shares.iter().for_each(|s| {
assert!(
s.lwe_ksk_share.as_ref().len()
s.lwe_ksk_share().as_ref().len()
== self.parameters().lwe_decomposition_count().0 * ring_size
);
lwe_modop.elwise_add_mut(lwe_ksk.as_mut(), s.lwe_ksk_share.as_ref());
lwe_modop.elwise_add_mut(lwe_ksk.as_mut(), s.lwe_ksk_share().as_ref());
});
lwe_ksk
};
@@ -1425,6 +1430,11 @@ where
M,
NonInteractiveMultiPartyCrs<[u8; 32]>,
> {
assert_eq!(
self.parameters().variant(),
&ParameterVariant::NonInteractiveMultiParty
);
// TODO: check whether parameters support `total_users`
let nttop = self.pbs_info().nttop_rlweq();
let rlwe_modop = self.pbs_info().modop_rlweq();
@@ -1539,15 +1549,15 @@ where
self._common_rountine_multi_party_lwe_ksk_share_gen(lwe_ksk_seed, &sk_rlwe, &sk_lwe)
};
CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare {
CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare::new(
ni_rgsw_cts,
ui_to_s_ksk,
others_ksk_zero_encs: zero_encs_for_others,
user_index: self_index,
zero_encs_for_others,
auto_keys_share,
lwe_ksk_share,
cr_seed: cr_seed.clone(),
}
self_index,
cr_seed.clone(),
)
}
fn _common_rountine_multi_party_auto_keys_share_gen(
@@ -1826,6 +1836,7 @@ where
S: PartialEq + Clone,
M: Clone,
{
assert_eq!(self.parameters().variant(), &ParameterVariant::MultiParty);
assert!(shares.len() > 0);
let parameters = shares[0].parameters().clone();
let cr_seed = shares[0].cr_seed();
@@ -3174,7 +3185,7 @@ mod tests {
);
let server_key_evaluation_domain =
NonInteractiveServerKeyEvaluationDomain::<_, _, DefaultSecureRng, NttBackendU64>::from(
seeded_server_key,
&seeded_server_key,
);
let mut ideal_rlwe = vec![0; ring_size];

View File

@@ -752,7 +752,7 @@ pub(super) mod impl_non_interactive_server_key_eval_domain {
impl<M, Rng, N>
From<
SeededNonInteractiveMultiPartyServerKey<
&SeededNonInteractiveMultiPartyServerKey<
M,
NonInteractiveMultiPartyCrs<Rng::Seed>,
BoolParameters<M::MatElement>,
@@ -769,7 +769,7 @@ pub(super) mod impl_non_interactive_server_key_eval_domain {
Rng::Seed: Clone + Copy + Default,
{
fn from(
value: SeededNonInteractiveMultiPartyServerKey<
value: &SeededNonInteractiveMultiPartyServerKey<
M,
NonInteractiveMultiPartyCrs<Rng::Seed>,
BoolParameters<M::MatElement>,
@@ -802,7 +802,7 @@ pub(super) mod impl_non_interactive_server_key_eval_domain {
assert!(auto_part_b.dimension() == (d_auto, ring_size));
let mut auto_ct = M::zeros(d_auto, ring_size);
let mut auto_ct = M::zeros(d_auto * 2, ring_size);
// sample part A
auto_ct.iter_rows_mut().take(d_auto).for_each(|ri| {
@@ -862,7 +862,7 @@ pub(super) mod impl_non_interactive_server_key_eval_domain {
.non_interactive_ui_to_s_key_switch_decomposition_count()
.0;
let total_users = *value.ui_to_s_ksks_key_order.iter().max().unwrap();
let ui_to_s_ksks = (0..total_users)
let ui_to_s_ksks = (0..total_users + 1)
.map(|user_index| {
let user_i_seed = value.cr_seed.ui_to_s_ks_seed_for_user_i::<Rng>(user_index);
let mut prng = Rng::new_with_seed(user_i_seed);
@@ -963,6 +963,12 @@ mod impl_shoup_non_interactive_server_key_eval_domain {
use super::*;
use crate::{backend::Modulus, pbs::PbsKey};
impl<M> ShoupNonInteractiveServerKeyEvaluationDomain<M> {
pub(in super::super) fn ui_to_s_ksk(&self, user_id: usize) -> &NormalAndShoup<M> {
&self.ui_to_s_ksks[user_id]
}
}
impl<M: Matrix + ToShoup<Modulus = M::MatElement>, R, N>
From<NonInteractiveServerKeyEvaluationDomain<M, BoolParameters<M::MatElement>, R, N>>
for ShoupNonInteractiveServerKeyEvaluationDomain<M>
@@ -974,23 +980,55 @@ mod impl_shoup_non_interactive_server_key_eval_domain {
) -> Self {
let rlwe_q = value.parameters.rlwe_q().q().unwrap();
let rgsw_dim = (
value.parameters.rlwe_rgsw_decomposition_count().0 .0 * 2
+ value.parameters.rlwe_rgsw_decomposition_count().1 .0 * 2,
value.parameters.rlwe_n().0,
);
let rgsw_cts = value
.rgsw_cts
.into_iter()
.map(|m| NormalAndShoup::new_with_modulus(m, rlwe_q))
.map(|m| {
assert!(m.dimension() == rgsw_dim);
NormalAndShoup::new_with_modulus(m, rlwe_q)
})
.collect_vec();
let auto_dim = (
value.parameters.auto_decomposition_count().0 * 2,
value.parameters.rlwe_n().0,
);
let mut auto_keys = HashMap::new();
value.auto_keys.into_iter().for_each(|(k, v)| {
assert!(v.dimension() == auto_dim);
auto_keys.insert(k, NormalAndShoup::new_with_modulus(v, rlwe_q));
});
let ui_ks_dim = (
value
.parameters
.non_interactive_ui_to_s_key_switch_decomposition_count()
.0
* 2,
value.parameters.rlwe_n().0,
);
let ui_to_s_ksks = value
.ui_to_s_ksks
.into_iter()
.map(|m| NormalAndShoup::new_with_modulus(m, rlwe_q))
.map(|m| {
assert!(m.dimension() == ui_ks_dim);
NormalAndShoup::new_with_modulus(m, rlwe_q)
})
.collect_vec();
assert!(
value.lwe_ksk.dimension()
== (
value.parameters.rlwe_n().0 * value.parameters.lwe_decomposition_count().0,
value.parameters.lwe_n().0 + 1
)
);
Self {
rgsw_cts,
auto_keys,
@@ -1006,7 +1044,8 @@ mod impl_shoup_non_interactive_server_key_eval_domain {
type RgswCt = NormalAndShoup<M>;
fn galois_key_for_auto(&self, k: usize) -> &Self::AutoKey {
self.auto_keys.get(&k).unwrap()
let d = self.auto_keys.get(&k).unwrap();
d
}
fn rgsw_ct_lwe_si(&self, si: usize) -> &Self::RgswCt {
&self.rgsw_cts[si]
@@ -1084,6 +1123,74 @@ mod shoup_server_key_eval_domain {
}
}
pub struct CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<M: Matrix, S> {
/// (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>,
auto_keys_share: HashMap<usize, M>,
lwe_ksk_share: M::R,
user_index: usize,
cr_seed: S,
}
mod impl_common_ref_non_interactive_multi_party_server_share {
use super::*;
impl<M: Matrix, S> CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<M, S> {
pub(in super::super) fn new(
ni_rgsw_cts: (Vec<M>, Vec<M>),
ui_to_s_ksk: M,
others_ksk_zero_encs: Vec<M>,
auto_keys_share: HashMap<usize, M>,
lwe_ksk_share: M::R,
user_index: usize,
cr_seed: S,
) -> Self {
Self {
ni_rgsw_cts,
ui_to_s_ksk,
others_ksk_zero_encs,
auto_keys_share,
lwe_ksk_share,
user_index,
cr_seed,
}
}
pub(in super::super) fn ni_rgsw_cts(&self) -> &(Vec<M>, Vec<M>) {
&self.ni_rgsw_cts
}
pub(in super::super) fn ui_to_s_ksk(&self) -> &M {
&self.ui_to_s_ksk
}
pub(in super::super) fn user_index(&self) -> usize {
self.user_index
}
pub(in super::super) fn auto_keys_share(&self) -> &HashMap<usize, M> {
&self.auto_keys_share
}
pub(in super::super) fn lwe_ksk_share(&self) -> &M::R {
&self.lwe_ksk_share
}
pub(in super::super) fn ui_to_s_ksk_zero_encs_for_user_i(&self, user_i: usize) -> &M {
assert!(user_i != self.user_index);
if user_i < self.user_index {
&self.others_ksk_zero_encs[user_i]
} else {
&self.others_ksk_zero_encs[user_i - 1]
}
}
}
}
/// Stores normal and shoup representation of Matrix elements (Normal, Shoup)
pub(crate) struct NormalAndShoup<M>(M, M);

View File

@@ -4,9 +4,14 @@ mod mp_api;
mod ni_mp_api;
mod noise;
pub(crate) mod parameters;
mod sp_api;
pub(crate) use keys::PublicKey;
pub type FheBool = Vec<u64>;
pub use ni_mp_api::*;
pub type ClientKey = keys::ClientKey<[u8; 32], u64>;
pub use mp_api::*;
pub enum ParameterSelector {
MultiPartyLessThanOrEqualTo16,
NonInteractiveMultiPartyLessThanOrEqualTo16,
}

View File

@@ -7,27 +7,32 @@ use crate::{
utils::{Global, WithLocal},
};
use super::{evaluator::*, keys::*, parameters::*};
use super::{evaluator::MultiPartyCrs, keys::*, parameters::*, ClientKey, ParameterSelector};
pub type BoolEvaluator = super::evaluator::BoolEvaluator<
Vec<Vec<u64>>,
NttBackendU64,
ModularOpsU64<CiphertextModulus<u64>>,
ModulusPowerOf2<CiphertextModulus<u64>>,
ShoupServerKeyEvaluationDomain<Vec<Vec<u64>>>,
>;
thread_local! {
static BOOL_EVALUATOR: RefCell<Option<BoolEvaluator<Vec<Vec<u64>>, NttBackendU64, ModularOpsU64<CiphertextModulus<u64>>, ModulusPowerOf2<CiphertextModulus<u64>>, ShoupServerKeyEvaluationDomain<Vec<Vec<u64>>>>>> = RefCell::new(None);
static BOOL_EVALUATOR: RefCell<Option<BoolEvaluator>> = RefCell::new(None);
}
static BOOL_SERVER_KEY: OnceLock<ShoupServerKeyEvaluationDomain<Vec<Vec<u64>>>> = OnceLock::new();
static MULTI_PARTY_CRS: OnceLock<MultiPartyCrs<[u8; 32]>> = OnceLock::new();
pub type ClientKey = super::keys::ClientKey<[u8; 32], u64>;
pub enum ParameterSelector {
MultiPartyLessThanOrEqualTo16,
}
pub fn set_parameter_set(select: ParameterSelector) {
match select {
ParameterSelector::MultiPartyLessThanOrEqualTo16 => {
BOOL_EVALUATOR.with_borrow_mut(|v| *v = Some(BoolEvaluator::new(SMALL_MP_BOOL_PARAMS)));
}
_ => {
panic!("Paramerters not supported")
}
}
}
@@ -134,15 +139,7 @@ impl Global for MultiPartyCrs<[u8; 32]> {
}
// BOOL EVALUATOR //
impl WithLocal
for BoolEvaluator<
Vec<Vec<u64>>,
NttBackendU64,
ModularOpsU64<CiphertextModulus<u64>>,
ModulusPowerOf2<CiphertextModulus<u64>>,
ShoupServerKeyEvaluationDomain<Vec<Vec<u64>>>,
>
{
impl WithLocal for BoolEvaluator {
fn with_local<F, R>(func: F) -> R
where
F: Fn(&Self) -> R,
@@ -174,74 +171,61 @@ impl Global for RuntimeServerKey {
mod impl_enc_dec {
use crate::{
bool::evaluator::BoolEncoding,
pbs::{sample_extract, PbsInfo},
rgsw::public_key_encrypt_rlwe,
Decryptor, Encryptor, Matrix, MatrixEntity, MultiPartyDecryptor, RowEntity,
Encryptor, Matrix, MatrixEntity, MultiPartyDecryptor, RowEntity,
};
use num_traits::Zero;
use itertools::Itertools;
use num_traits::{ToPrimitive, Zero};
use super::*;
type Mat = Vec<Vec<u64>>;
impl<E> Encryptor<bool, Vec<u64>> for super::super::keys::ClientKey<[u8; 32], E> {
fn encrypt(&self, m: &bool) -> Vec<u64> {
BoolEvaluator::with_local(|e| e.sk_encrypt(*m, self))
}
}
impl<E> Decryptor<bool, Vec<u64>> for super::super::keys::ClientKey<[u8; 32], E> {
fn decrypt(&self, c: &Vec<u64>) -> bool {
BoolEvaluator::with_local(|e| e.sk_decrypt(c, self))
}
}
impl<E> MultiPartyDecryptor<bool, <Mat as Matrix>::R>
for super::super::keys::ClientKey<[u8; 32], E>
{
type DecryptionShare = <Mat as Matrix>::MatElement;
fn gen_decryption_share(&self, c: &<Mat as Matrix>::R) -> Self::DecryptionShare {
BoolEvaluator::with_local(|e| e.multi_party_decryption_share(c, self))
}
fn aggregate_decryption_shares(
&self,
c: &<Mat as Matrix>::R,
shares: &[Self::DecryptionShare],
) -> bool {
BoolEvaluator::with_local(|e| e.multi_party_decrypt(shares, c))
}
}
impl<Rng, ModOp> Encryptor<[bool], Mat> for PublicKey<Mat, Rng, ModOp> {
fn encrypt(&self, m: &[bool]) -> Mat {
impl<Rng, ModOp> Encryptor<[bool], Vec<Mat>> for PublicKey<Mat, Rng, ModOp> {
fn encrypt(&self, m: &[bool]) -> Vec<Mat> {
BoolEvaluator::with_local(|e| {
DefaultSecureRng::with_local_mut(|rng| {
let parameters = e.parameters();
let mut rlwe_out = <Mat as MatrixEntity>::zeros(2, parameters.rlwe_n().0);
assert!(m.len() <= parameters.rlwe_n().0);
let ring_size = parameters.rlwe_n().0;
let mut message =
vec![<Mat as Matrix>::MatElement::zero(); parameters.rlwe_n().0];
m.iter().enumerate().for_each(|(i, v)| {
if *v {
message[i] = parameters.rlwe_q().true_el()
} else {
message[i] = parameters.rlwe_q().false_el()
}
});
let rlwe_count = ((m.len() as f64 / ring_size as f64).ceil())
.to_usize()
.unwrap();
// e.pk_encrypt_batched(self.key(), m)
public_key_encrypt_rlwe::<_, _, _, _, i32, _>(
&mut rlwe_out,
self.key(),
&message,
e.pbs_info().modop_rlweq(),
e.pbs_info().nttop_rlweq(),
rng,
);
rlwe_out
// encrypt `m` into ceil(len(m)/N) RLWE ciphertexts
let rlwes = (0..rlwe_count)
.map(|index| {
let mut message = vec![<Mat as Matrix>::MatElement::zero(); ring_size];
m[(index * ring_size)..std::cmp::min(m.len(), (index + 1) * ring_size)]
.iter()
.enumerate()
.for_each(|(i, v)| {
if *v {
message[i] = parameters.rlwe_q().true_el()
} else {
message[i] = parameters.rlwe_q().false_el()
}
});
// encrypt message
let mut rlwe_out =
<Mat as MatrixEntity>::zeros(2, parameters.rlwe_n().0);
public_key_encrypt_rlwe::<_, _, _, _, i32, _>(
&mut rlwe_out,
self.key(),
&message,
e.pbs_info().modop_rlweq(),
e.pbs_info().nttop_rlweq(),
rng,
);
rlwe_out
})
.collect_vec();
rlwes
})
})
}
@@ -250,10 +234,10 @@ mod impl_enc_dec {
impl<Rng, ModOp> Encryptor<bool, <Mat as Matrix>::R> for PublicKey<Mat, Rng, ModOp> {
fn encrypt(&self, m: &bool) -> <Mat as Matrix>::R {
let m = vec![*m];
let rlwe = self.encrypt(m.as_slice());
let rlwe = &self.encrypt(m.as_slice())[0];
BoolEvaluator::with_local(|e| {
let mut lwe = <Mat as Matrix>::R::zeros(e.parameters().rlwe_n().0 + 1);
sample_extract(&mut lwe, &rlwe, e.pbs_info().modop_rlweq(), 0);
sample_extract(&mut lwe, rlwe, e.pbs_info().modop_rlweq(), 0);
lwe
})
}

View File

@@ -1,66 +1,544 @@
use std::{cell::RefCell, sync::OnceLock};
use crate::{
backend::ModulusPowerOf2,
bool::parameters::ParameterVariant,
random::DefaultSecureRng,
utils::{Global, WithLocal},
ModularOpsU64, NttBackendU64,
};
use super::{
evaluator::NonInteractiveMultiPartyCrs,
keys::{
CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare,
NonInteractiveServerKeyEvaluationDomain, SeededNonInteractiveMultiPartyServerKey,
ShoupNonInteractiveServerKeyEvaluationDomain,
},
parameters::{BoolParameters, CiphertextModulus, NON_INTERACTIVE_SMALL_MP_BOOL_PARAMS},
ClientKey, ParameterSelector,
};
pub type BoolEvaluator = super::evaluator::BoolEvaluator<
Vec<Vec<u64>>,
NttBackendU64,
ModularOpsU64<CiphertextModulus<u64>>,
ModulusPowerOf2<CiphertextModulus<u64>>,
ShoupNonInteractiveServerKeyEvaluationDomain<Vec<Vec<u64>>>,
>;
thread_local! {
static BOOL_EVALUATOR: RefCell<Option<BoolEvaluator>> = RefCell::new(None);
}
static BOOL_SERVER_KEY: OnceLock<ShoupNonInteractiveServerKeyEvaluationDomain<Vec<Vec<u64>>>> =
OnceLock::new();
static MULTI_PARTY_CRS: OnceLock<NonInteractiveMultiPartyCrs<[u8; 32]>> = OnceLock::new();
pub fn set_parameter_set(select: ParameterSelector) {
match select {
ParameterSelector::NonInteractiveMultiPartyLessThanOrEqualTo16 => {
BOOL_EVALUATOR.with_borrow_mut(|v| {
*v = Some(BoolEvaluator::new(NON_INTERACTIVE_SMALL_MP_BOOL_PARAMS))
});
}
_ => {
panic!("Paramerters not supported")
}
}
}
pub fn set_common_reference_seed(seed: [u8; 32]) {
BoolEvaluator::with_local(|e| {
assert_eq!(
e.parameters().variant(),
&ParameterVariant::NonInteractiveMultiParty,
"Set parameters do not support Non interactive multi-party"
);
});
assert!(
MULTI_PARTY_CRS
.set(NonInteractiveMultiPartyCrs { seed: seed })
.is_ok(),
"Attempted to set MP SEED twice."
)
}
pub fn gen_client_key() -> ClientKey {
BoolEvaluator::with_local(|e| e.client_key())
}
pub fn gen_server_key_share(
user_id: usize,
total_users: usize,
client_key: &ClientKey,
) -> CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<
Vec<Vec<u64>>,
NonInteractiveMultiPartyCrs<[u8; 32]>,
> {
BoolEvaluator::with_local(|e| {
let cr_seed = NonInteractiveMultiPartyCrs::global();
e.non_interactive_multi_party_key_share(cr_seed, user_id, total_users, client_key)
})
}
pub fn aggregate_server_key_shares(
shares: &[CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<
Vec<Vec<u64>>,
NonInteractiveMultiPartyCrs<[u8; 32]>,
>],
) -> SeededNonInteractiveMultiPartyServerKey<
Vec<Vec<u64>>,
NonInteractiveMultiPartyCrs<[u8; 32]>,
BoolParameters<u64>,
> {
BoolEvaluator::with_local(|e| {
let cr_seed = NonInteractiveMultiPartyCrs::global();
e.aggregate_non_interactive_multi_party_key_share(cr_seed, shares.len(), shares)
})
}
impl
SeededNonInteractiveMultiPartyServerKey<
Vec<Vec<u64>>,
NonInteractiveMultiPartyCrs<[u8; 32]>,
BoolParameters<u64>,
>
{
pub fn set_server_key(&self) {
let eval_key = NonInteractiveServerKeyEvaluationDomain::<
_,
BoolParameters<u64>,
DefaultSecureRng,
NttBackendU64,
>::from(self);
assert!(
BOOL_SERVER_KEY
.set(ShoupNonInteractiveServerKeyEvaluationDomain::from(eval_key))
.is_ok(),
"Attempted to set server key twice!"
);
}
}
impl Global for NonInteractiveMultiPartyCrs<[u8; 32]> {
fn global() -> &'static Self {
MULTI_PARTY_CRS
.get()
.expect("Non-interactive multi-party common reference string not set")
}
}
// BOOL EVALUATOR //
impl WithLocal for BoolEvaluator {
fn with_local<F, R>(func: F) -> R
where
F: Fn(&Self) -> R,
{
BOOL_EVALUATOR.with_borrow(|s| func(s.as_ref().expect("Parameters not set")))
}
fn with_local_mut<F, R>(func: F) -> R
where
F: Fn(&mut Self) -> R,
{
BOOL_EVALUATOR.with_borrow_mut(|s| func(s.as_mut().expect("Parameters not set")))
}
fn with_local_mut_mut<F, R>(func: &mut F) -> R
where
F: FnMut(&mut Self) -> R,
{
BOOL_EVALUATOR.with_borrow_mut(|s| func(s.as_mut().expect("Parameters not set")))
}
}
pub(crate) type RuntimeServerKey = ShoupNonInteractiveServerKeyEvaluationDomain<Vec<Vec<u64>>>;
impl Global for RuntimeServerKey {
fn global() -> &'static Self {
BOOL_SERVER_KEY.get().expect("Server key not set!")
}
}
/// Non interactive multi-party specfic encryptor decryptor routines
mod impl_enc_dec {
use crate::{
bool::{
evaluator::{BoolEncoding, BoolEvaluator},
keys::NonInteractiveMultiPartyClientKey,
parameters::CiphertextModulus,
},
pbs::PbsInfo,
random::{DefaultSecureRng, NewWithSeed},
rgsw::secret_key_encrypt_rlwe,
bool::{evaluator::BoolEncoding, keys::NonInteractiveMultiPartyClientKey},
pbs::{sample_extract, PbsInfo, WithShoupRepr},
random::{DefaultSecureRng, NewWithSeed, RandomFillUniformInModulus},
rgsw::{key_switch, secret_key_encrypt_rlwe},
utils::{TryConvertFrom1, WithLocal},
Encryptor, Matrix, RowEntity,
Encryptor, KeySwitchWithId, Matrix, MatrixEntity, MatrixMut, MultiPartyDecryptor,
RowEntity, RowMut,
};
use num_traits::Zero;
use itertools::Itertools;
use num_traits::{ToPrimitive, Zero};
trait SeededCiphertext<M, S> {
fn new_with_seed(data: M, seed: S) -> Self;
}
use super::*;
type Mat = Vec<Vec<u64>>;
impl<K, C> Encryptor<[bool], C> for K
pub(super) struct BatchedFheBools<C> {
pub(super) data: Vec<C>,
}
impl<C: MatrixMut<MatElement = u64>> BatchedFheBools<C>
where
C::R: RowEntity + RowMut,
{
pub(super) fn extract(&self, index: usize) -> C::R {
BoolEvaluator::with_local(|e| {
let ring_size = e.parameters().rlwe_n().0;
let ct_index = index / ring_size;
let coeff_index = index % ring_size;
let mut lwe_out = C::R::zeros(e.parameters().rlwe_n().0 + 1);
sample_extract(
&mut lwe_out,
&self.data[ct_index],
e.pbs_info().modop_rlweq(),
coeff_index,
);
lwe_out
})
}
}
pub(super) struct NonInteractiveBatchedFheBools<C> {
data: Vec<C>,
}
impl<M: MatrixEntity + MatrixMut<MatElement = u64>> From<&(Vec<M::R>, [u8; 32])>
for NonInteractiveBatchedFheBools<M>
where
<M as Matrix>::R: RowMut,
{
fn from(value: &(Vec<M::R>, [u8; 32])) -> Self {
BoolEvaluator::with_local(|e| {
let parameters = e.parameters();
let ring_size = parameters.rlwe_n().0;
let rlwe_q = parameters.rlwe_q();
let mut prng = DefaultSecureRng::new_seeded(value.1);
let rlwes = value
.0
.iter()
.map(|partb| {
let mut rlwe = M::zeros(2, ring_size);
// sample A
RandomFillUniformInModulus::random_fill(
&mut prng,
rlwe_q,
rlwe.get_row_mut(0),
);
// Copy over B
rlwe.get_row_mut(1).copy_from_slice(partb.as_ref());
rlwe
})
.collect_vec();
Self { data: rlwes }
})
}
}
impl<K> Encryptor<[bool], NonInteractiveBatchedFheBools<Mat>> for K
where
K: Encryptor<[bool], (Mat, [u8; 32])>,
{
fn encrypt(&self, m: &[bool]) -> NonInteractiveBatchedFheBools<Mat> {
NonInteractiveBatchedFheBools::from(&K::encrypt(&self, m))
}
}
impl<K> Encryptor<[bool], (Mat, [u8; 32])> for K
where
K: NonInteractiveMultiPartyClientKey,
C: SeededCiphertext<<Mat as Matrix>::R, <DefaultSecureRng as NewWithSeed>::Seed>,
<Mat as Matrix>::R:
TryConvertFrom1<[K::Element], CiphertextModulus<<Mat as Matrix>::MatElement>>,
{
fn encrypt(&self, m: &[bool]) -> C {
fn encrypt(&self, m: &[bool]) -> (Mat, [u8; 32]) {
BoolEvaluator::with_local(|e| {
let parameters = e.parameters();
assert!(m.len() <= parameters.rlwe_n().0);
let mut message = vec![<Mat as Matrix>::MatElement::zero(); parameters.rlwe_n().0];
m.iter().enumerate().for_each(|(i, v)| {
if *v {
message[i] = parameters.rlwe_q().true_el()
} else {
message[i] = parameters.rlwe_q().false_el()
}
});
DefaultSecureRng::with_local_mut(|rng| {
let parameters = e.parameters();
let ring_size = parameters.rlwe_n().0;
let rlwe_count = ((m.len() as f64 / ring_size as f64).ceil())
.to_usize()
.unwrap();
let mut seed = <DefaultSecureRng as NewWithSeed>::Seed::default();
rng.fill_bytes(&mut seed);
let mut prng = DefaultSecureRng::new_seeded(seed);
let mut rlwe_out =
<<Mat as Matrix>::R as RowEntity>::zeros(parameters.rlwe_n().0);
let sk_u = self.sk_u_rlwe();
secret_key_encrypt_rlwe(
&message,
&mut rlwe_out,
&self.sk_u_rlwe(),
e.pbs_info().modop_rlweq(),
e.pbs_info().nttop_rlweq(),
&mut prng,
rng,
);
// encrypt `m` into ceil(len(m)/N) RLWE ciphertexts
let rlwes = (0..rlwe_count)
.map(|index| {
let mut message = vec![<Mat as Matrix>::MatElement::zero(); ring_size];
m[(index * ring_size)..std::cmp::min(m.len(), (index + 1) * ring_size)]
.iter()
.enumerate()
.for_each(|(i, v)| {
if *v {
message[i] = parameters.rlwe_q().true_el()
} else {
message[i] = parameters.rlwe_q().false_el()
}
});
C::new_with_seed(rlwe_out, seed)
// encrypt message
let mut rlwe_out =
<<Mat as Matrix>::R as RowEntity>::zeros(parameters.rlwe_n().0);
secret_key_encrypt_rlwe(
&message,
&mut rlwe_out,
&sk_u,
e.pbs_info().modop_rlweq(),
e.pbs_info().nttop_rlweq(),
&mut prng,
rng,
);
rlwe_out
})
.collect_vec();
(rlwes, seed)
})
})
}
}
impl KeySwitchWithId<Mat> for Mat {
fn key_switch(&self, user_id: usize) -> Mat {
BoolEvaluator::with_local(|e| {
let server_key = BOOL_SERVER_KEY.get().unwrap();
let ksk = server_key.ui_to_s_ksk(user_id);
let decomposer = e.ni_ui_to_s_ks_decomposer().as_ref().unwrap();
// perform key switch
key_switch(
self,
ksk.as_ref(),
ksk.shoup_repr(),
decomposer,
e.pbs_info().nttop_rlweq(),
e.pbs_info().modop_rlweq(),
)
})
}
}
impl<C> KeySwitchWithId<BatchedFheBools<C>> for NonInteractiveBatchedFheBools<C>
where
C: KeySwitchWithId<C>,
{
fn key_switch(&self, user_id: usize) -> BatchedFheBools<C> {
let data = self
.data
.iter()
.map(|c| c.key_switch(user_id))
.collect_vec();
BatchedFheBools { data }
}
}
impl<E> MultiPartyDecryptor<bool, <Mat as Matrix>::R>
for super::super::keys::ClientKey<[u8; 32], E>
{
type DecryptionShare = <Mat as Matrix>::MatElement;
fn gen_decryption_share(&self, c: &<Mat as Matrix>::R) -> Self::DecryptionShare {
BoolEvaluator::with_local(|e| e.multi_party_decryption_share(c, self))
}
fn aggregate_decryption_shares(
&self,
c: &<Mat as Matrix>::R,
shares: &[Self::DecryptionShare],
) -> bool {
BoolEvaluator::with_local(|e| e.multi_party_decrypt(shares, c))
}
}
}
#[cfg(test)]
mod tests {
use impl_enc_dec::NonInteractiveBatchedFheBools;
use itertools::{izip, Itertools};
use num_traits::ToPrimitive;
use rand::{thread_rng, RngCore};
use crate::{
backend::Modulus,
bool::{
evaluator::{BoolEncoding, BooleanGates},
keys::SinglePartyClientKey,
},
lwe::decrypt_lwe,
rgsw::decrypt_rlwe,
utils::{Stats, TryConvertFrom1},
ArithmeticOps, Encryptor, KeySwitchWithId, ModInit, MultiPartyDecryptor, NttInit,
VectorOps,
};
use super::*;
#[test]
fn non_interactive_mp_bool_nand() {
set_parameter_set(ParameterSelector::NonInteractiveMultiPartyLessThanOrEqualTo16);
let mut seed = [0u8; 32];
thread_rng().fill_bytes(&mut seed);
set_common_reference_seed(seed);
let parties = 2;
let cks = (0..parties).map(|_| gen_client_key()).collect_vec();
let key_shares = cks
.iter()
.enumerate()
.map(|(user_index, ck)| gen_server_key_share(user_index, parties, ck))
.collect_vec();
let seeded_server_key = aggregate_server_key_shares(&key_shares);
seeded_server_key.set_server_key();
let parameters = BoolEvaluator::with_local(|e| e.parameters().clone());
let nttop = NttBackendU64::new(parameters.rlwe_q(), parameters.rlwe_n().0);
let rlwe_q_modop = ModularOpsU64::new(*parameters.rlwe_q());
let mut ideal_rlwe_sk = vec![0i32; parameters.rlwe_n().0];
cks.iter().for_each(|k| {
let sk_rlwe = k.sk_rlwe();
izip!(ideal_rlwe_sk.iter_mut(), sk_rlwe.iter()).for_each(|(a, b)| {
*a = *a + b;
});
});
let mut m0 = false;
let mut m1 = true;
let mut ct0 = {
let ct: NonInteractiveBatchedFheBools<_> = cks[0].encrypt(vec![m0].as_slice());
let ct = ct.key_switch(0);
ct.extract(0)
};
let mut ct1 = {
let ct: NonInteractiveBatchedFheBools<_> = cks[1].encrypt(vec![m1].as_slice());
let ct = ct.key_switch(1);
ct.extract(0)
};
for _ in 0..100 {
let ct_out =
BoolEvaluator::with_local_mut(|e| e.xor(&ct0, &ct1, RuntimeServerKey::global()));
let decryption_shares = cks
.iter()
.map(|k| k.gen_decryption_share(&ct_out))
.collect_vec();
let m_out = cks[0].aggregate_decryption_shares(&ct_out, &decryption_shares);
let m_expected = (m0 ^ m1);
{
let noisy_m = decrypt_lwe(&ct_out, &ideal_rlwe_sk, &rlwe_q_modop);
let noise = if m_expected {
rlwe_q_modop.sub(&parameters.rlwe_q().true_el(), &noisy_m)
} else {
rlwe_q_modop.sub(&parameters.rlwe_q().false_el(), &noisy_m)
};
println!(
"Noise: {}",
parameters
.rlwe_q()
.map_element_to_i64(&noise)
.abs()
.to_f64()
.unwrap()
.log2()
)
}
assert!(m_out == m_expected, "Expected {m_expected} but got {m_out}");
m1 = m0;
m0 = m_out;
ct1 = ct0;
ct0 = ct_out;
}
}
#[test]
fn trialtest() {
set_parameter_set(ParameterSelector::NonInteractiveMultiPartyLessThanOrEqualTo16);
set_common_reference_seed([2; 32]);
let parties = 2;
let cks = (0..parties).map(|_| gen_client_key()).collect_vec();
let key_shares = cks
.iter()
.enumerate()
.map(|(user_index, ck)| gen_server_key_share(user_index, parties, ck))
.collect_vec();
let seeded_server_key = aggregate_server_key_shares(&key_shares);
seeded_server_key.set_server_key();
let m = vec![false, true];
let ct: NonInteractiveBatchedFheBools<_> = cks[0].encrypt(m.as_slice());
let ct = ct.key_switch(0);
let parameters = BoolEvaluator::with_local(|e| e.parameters().clone());
let nttop = NttBackendU64::new(parameters.rlwe_q(), parameters.rlwe_n().0);
let rlwe_q_modop = ModularOpsU64::new(*parameters.rlwe_q());
let mut ideal_rlwe_sk = vec![0i32; parameters.rlwe_n().0];
cks.iter().for_each(|k| {
let sk_rlwe = k.sk_rlwe();
izip!(ideal_rlwe_sk.iter_mut(), sk_rlwe.iter()).for_each(|(a, b)| {
*a = *a + b;
});
});
let message = m
.iter()
.map(|b| {
if *b {
parameters.rlwe_q().true_el()
} else {
parameters.rlwe_q().false_el()
}
})
.collect_vec();
let mut m_out = vec![0u64; parameters.rlwe_n().0];
decrypt_rlwe(
&ct.data[0],
&ideal_rlwe_sk,
&mut m_out,
&nttop,
&rlwe_q_modop,
);
let mut diff = m_out;
rlwe_q_modop.elwise_sub_mut(diff.as_mut_slice(), message.as_ref());
let mut stats = Stats::new();
stats.add_more(&Vec::<i64>::try_convert_from(
diff.as_slice(),
parameters.rlwe_q(),
));
println!("Noise: {}", stats.std_dev().abs().log2());
}
}

View File

@@ -303,6 +303,10 @@ impl<El> BoolParameters<El> {
});
els
}
pub(crate) fn variant(&self) -> &ParameterVariant {
&self.variant
}
}
#[derive(Clone, Copy, PartialEq)]
@@ -506,7 +510,7 @@ pub(crate) const NON_INTERACTIVE_SMALL_MP_BOOL_PARAMS: BoolParameters<u64> = Boo
lwe_q: CiphertextModulus::new_non_native(1 << 20),
br_q: 1 << 11,
rlwe_n: PolynomialSize(1 << 11),
lwe_n: LweDimension(10),
lwe_n: LweDimension(600),
lwe_decomposer_params: (DecompostionLogBase(4), DecompositionCount(5)),
rlrg_decomposer_params: (
DecompostionLogBase(11),

25
src/bool/sp_api.rs Normal file
View File

@@ -0,0 +1,25 @@
mod impl_enc_dec {
use crate::{Decryptor, Encryptor};
use super::super::keys::SinglePartyClientKey;
impl<K> Encryptor<bool, Vec<u64>> for K
where
K: SinglePartyClientKey,
{
fn encrypt(&self, m: &bool) -> Vec<u64> {
todo!()
// BoolEvaluator::with_local(|e| e.sk_encrypt(*m, self))
}
}
impl<K> Decryptor<bool, Vec<u64>> for K
where
K: SinglePartyClientKey,
{
fn decrypt(&self, c: &Vec<u64>) -> bool {
todo!()
// BoolEvaluator::with_local(|e| e.sk_decrypt(c, self))
}
}
}