mirror of
https://github.com/arnaucube/phantom-zone.git
synced 2026-01-09 15:41:30 +01:00
non interactive mp works
This commit is contained in:
@@ -6,9 +6,9 @@ fn plain_circuit(a: u8, b: u8, c: u8) -> u8 {
|
||||
(a + b) * c
|
||||
}
|
||||
|
||||
fn fhe_circuit(fhe_a: &FheUint8, fhe_b: &FheUint8, fhe_c: &FheUint8) -> FheUint8 {
|
||||
&(fhe_a + fhe_b) * fhe_c
|
||||
}
|
||||
// fn fhe_circuit(fhe_a: &FheUint8, fhe_b: &FheUint8, fhe_c: &FheUint8) ->
|
||||
// FheUint8 { &(fhe_a + fhe_b) * fhe_c
|
||||
// }
|
||||
|
||||
fn main() {
|
||||
set_parameter_set(ParameterSelector::MultiPartyLessThanOrEqualTo16);
|
||||
@@ -50,22 +50,25 @@ fn main() {
|
||||
let fhe_b = public_key.encrypt(&b);
|
||||
let fhe_c = public_key.encrypt(&c);
|
||||
|
||||
let fhe_batched = public_key.encrypt(vec![12, 3u8].as_slice());
|
||||
|
||||
// fhe evaluation
|
||||
let now = std::time::Instant::now();
|
||||
let fhe_out = fhe_circuit(&fhe_a, &fhe_b, &fhe_c);
|
||||
println!("Circuit time: {:?}", now.elapsed());
|
||||
// let now = std::time::Instant::now();
|
||||
// let fhe_out = fhe_circuit(&fhe_a, &fhe_b, &fhe_c);
|
||||
// println!("Circuit time: {:?}", now.elapsed());
|
||||
|
||||
// plain evaluation
|
||||
let out = plain_circuit(a, b, c);
|
||||
// // plain evaluation
|
||||
// let out = plain_circuit(a, b, c);
|
||||
|
||||
// generate decryption shares to decrypt ciphertext fhe_out
|
||||
let decryption_shares = client_keys
|
||||
.iter()
|
||||
.map(|k| k.gen_decryption_share(&fhe_out))
|
||||
.collect_vec();
|
||||
// // generate decryption shares to decrypt ciphertext fhe_out
|
||||
// let decryption_shares = client_keys
|
||||
// .iter()
|
||||
// .map(|k| k.gen_decryption_share(&fhe_out))
|
||||
// .collect_vec();
|
||||
|
||||
// decrypt fhe_out using decryption shares
|
||||
let got_out = client_keys[0].aggregate_decryption_shares(&fhe_out, &decryption_shares);
|
||||
// // decrypt fhe_out using decryption shares
|
||||
// let got_out = client_keys[0].aggregate_decryption_shares(&fhe_out,
|
||||
// &decryption_shares);
|
||||
|
||||
assert_eq!(got_out, out);
|
||||
// assert_eq!(got_out, out);
|
||||
}
|
||||
|
||||
@@ -231,7 +231,12 @@ impl<T> VectorOps for ModularOpsU64<T> {
|
||||
impl<R: RowMut<Element = u64>, T> ShoupMatrixFMA<R> for ModularOpsU64<T> {
|
||||
fn shoup_matrix_fma(&self, out: &mut [R::Element], a: &[R], a_shoup: &[R], b: &[R]) {
|
||||
assert!(a.len() == a_shoup.len());
|
||||
assert!(a.len() == b.len());
|
||||
assert!(
|
||||
a.len() == b.len(),
|
||||
"Unequal length {}!={}",
|
||||
a.len(),
|
||||
b.len()
|
||||
);
|
||||
|
||||
let q = self.q;
|
||||
let q_twice = self.q << 1;
|
||||
|
||||
@@ -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(¶meters);
|
||||
|
||||
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];
|
||||
|
||||
121
src/bool/keys.rs
121
src/bool/keys.rs
@@ -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);
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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(¶meters.rlwe_q().true_el(), &noisy_m)
|
||||
} else {
|
||||
rlwe_q_modop.sub(¶meters.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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
25
src/bool/sp_api.rs
Normal 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))
|
||||
}
|
||||
}
|
||||
}
|
||||
13
src/lib.rs
13
src/lib.rs
@@ -19,13 +19,12 @@ mod utils;
|
||||
pub use backend::{
|
||||
ArithmeticLazyOps, ArithmeticOps, ModInit, ModularOpsU64, ShoupMatrixFMA, VectorOps,
|
||||
};
|
||||
pub use bool::{
|
||||
aggregate_public_key_shares, aggregate_server_key_shares, gen_client_key, gen_mp_keys_phase1,
|
||||
gen_mp_keys_phase2, set_mp_seed, set_parameter_set, ParameterSelector,
|
||||
};
|
||||
// pub use bool::{
|
||||
// aggregate_public_key_shares, aggregate_server_key_shares, gen_client_key,
|
||||
// gen_mp_keys_phase1, gen_mp_keys_phase2, set_mp_seed, set_parameter_set,
|
||||
// ParameterSelector, };
|
||||
pub use decomposer::{Decomposer, DecomposerIter, DefaultDecomposer};
|
||||
pub use ntt::{Ntt, NttBackendU64, NttInit};
|
||||
pub use shortint::FheUint8;
|
||||
|
||||
pub trait Matrix: AsRef<[Self::R]> {
|
||||
type MatElement;
|
||||
@@ -180,3 +179,7 @@ pub trait MultiPartyDecryptor<M, C> {
|
||||
fn gen_decryption_share(&self, c: &C) -> Self::DecryptionShare;
|
||||
fn aggregate_decryption_shares(&self, c: &C, shares: &[Self::DecryptionShare]) -> M;
|
||||
}
|
||||
|
||||
pub trait KeySwitchWithId<C> {
|
||||
fn key_switch(&self, user_id: usize) -> C;
|
||||
}
|
||||
|
||||
@@ -392,6 +392,7 @@ pub(crate) fn sample_extract<M: Matrix + MatrixMut, ModOp: ArithmeticOps<Element
|
||||
M::MatElement: Copy,
|
||||
{
|
||||
let ring_size = rlwe_in.dimension().1;
|
||||
assert!(ring_size + 1 == lwe_out.as_ref().len());
|
||||
|
||||
// index..=0
|
||||
let to = &mut lwe_out.as_mut()[1..];
|
||||
|
||||
@@ -17,8 +17,9 @@ use crate::{
|
||||
DefaultSecureRng, NewWithSeed, RandomElementInModulus, RandomFill,
|
||||
RandomFillGaussianInModulus, RandomFillUniformInModulus,
|
||||
},
|
||||
rgsw::decompose_r,
|
||||
utils::{fill_random_ternary_secret_with_hamming_weight, TryConvertFrom1, WithLocal},
|
||||
Matrix, MatrixEntity, MatrixMut, Row, RowEntity, RowMut, Secret,
|
||||
Matrix, MatrixEntity, MatrixMut, Row, RowEntity, RowMut, Secret, ShoupMatrixFMA,
|
||||
};
|
||||
|
||||
pub(crate) fn generate_auto_map(ring_size: usize, k: isize) -> (Vec<usize>, Vec<bool>) {
|
||||
|
||||
@@ -1061,8 +1061,8 @@ pub(crate) mod tests {
|
||||
let ring_size = 1 << 11;
|
||||
let q = generate_prime(logq, ring_size, 1u64 << logq).unwrap();
|
||||
let p = 1u64 << logp;
|
||||
let d_rgsw = 5;
|
||||
let logb = 12;
|
||||
let d_rgsw = 12;
|
||||
let logb = 5;
|
||||
|
||||
let s = RlweSecret::random((ring_size >> 1) as usize, ring_size as usize);
|
||||
|
||||
|
||||
@@ -647,3 +647,67 @@ pub(crate) fn rgsw_by_rgsw_inplace<
|
||||
.iter_rows_mut()
|
||||
.for_each(|ri| ntt_op.backward(ri.as_mut()));
|
||||
}
|
||||
|
||||
pub(crate) fn key_switch<
|
||||
M: MatrixMut + MatrixEntity,
|
||||
ModOp: GetModulus<Element = M::MatElement> + ShoupMatrixFMA<M::R> + VectorOps<Element = M::MatElement>,
|
||||
NttOp: Ntt<Element = M::MatElement>,
|
||||
D: Decomposer<Element = M::MatElement>,
|
||||
>(
|
||||
rlwe_in: &M,
|
||||
ksk: &M,
|
||||
ksk_shoup: &M,
|
||||
decomposer: &D,
|
||||
ntt_op: &NttOp,
|
||||
mod_op: &ModOp,
|
||||
) -> M
|
||||
where
|
||||
<M as Matrix>::R: RowMut + RowEntity,
|
||||
M::MatElement: Copy,
|
||||
{
|
||||
let ring_size = rlwe_in.dimension().1;
|
||||
assert!(rlwe_in.dimension().0 == 2);
|
||||
assert!(ksk.dimension() == (decomposer.decomposition_count() * 2, ring_size));
|
||||
|
||||
let mut rlwe_out = M::zeros(2, ring_size);
|
||||
|
||||
let mut tmp = M::zeros(decomposer.decomposition_count(), ring_size);
|
||||
let mut tmp_row = M::R::zeros(ring_size);
|
||||
|
||||
// key switch RLWE part -A
|
||||
// negative A
|
||||
tmp_row.as_mut().copy_from_slice(rlwe_in.get_row_slice(0));
|
||||
mod_op.elwise_neg_mut(tmp_row.as_mut());
|
||||
// decompose -A and send to evaluation domain
|
||||
decompose_r(tmp_row.as_ref(), tmp.as_mut(), decomposer);
|
||||
tmp.iter_rows_mut()
|
||||
.for_each(|r| ntt_op.forward_lazy(r.as_mut()));
|
||||
|
||||
// RLWE_s(-A u) = B' + B, A' = (decomp(-A) * Ksk(u -> s)) + (B, 0)
|
||||
let (ksk_part_a, ksk_part_b) = ksk.split_at_row(decomposer.decomposition_count());
|
||||
let (ksk_part_a_shoup, ksk_part_b_shoup) =
|
||||
ksk_shoup.split_at_row(decomposer.decomposition_count());
|
||||
// Part A'
|
||||
mod_op.shoup_matrix_fma(
|
||||
rlwe_out.get_row_mut(0),
|
||||
&ksk_part_a,
|
||||
&ksk_part_a_shoup,
|
||||
tmp.as_ref(),
|
||||
);
|
||||
// Part B'
|
||||
mod_op.shoup_matrix_fma(
|
||||
rlwe_out.get_row_mut(1),
|
||||
&ksk_part_b,
|
||||
&ksk_part_b_shoup,
|
||||
tmp.as_ref(),
|
||||
);
|
||||
// back to coefficient domain
|
||||
rlwe_out
|
||||
.iter_rows_mut()
|
||||
.for_each(|r| ntt_op.backward(r.as_mut()));
|
||||
|
||||
// B' + B
|
||||
mod_op.elwise_add_mut(rlwe_out.get_row_mut(1), rlwe_in.get_row_slice(1));
|
||||
|
||||
rlwe_out
|
||||
}
|
||||
|
||||
196
src/shortint/enc_dec.rs
Normal file
196
src/shortint/enc_dec.rs
Normal file
@@ -0,0 +1,196 @@
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::{
|
||||
bool::BoolEvaluator,
|
||||
random::{DefaultSecureRng, RandomFillUniformInModulus},
|
||||
utils::{TryConvertFrom1, WithLocal},
|
||||
Decryptor, Encryptor, KeySwitchWithId, Matrix, MatrixEntity, MatrixMut, MultiPartyDecryptor,
|
||||
RowMut,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FheUint8<C> {
|
||||
pub(super) data: Vec<C>,
|
||||
}
|
||||
|
||||
impl<C> FheUint8<C> {
|
||||
pub(super) fn data(&self) -> &[C] {
|
||||
&self.data
|
||||
}
|
||||
|
||||
pub(super) fn data_mut(&mut self) -> &mut [C] {
|
||||
&mut self.data
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BatchedFheUint8<C> {
|
||||
data: Vec<C>,
|
||||
}
|
||||
|
||||
impl<M: MatrixEntity + MatrixMut<MatElement = u64>> From<&SeededBatchedFheUint8<M::R, [u8; 32]>>
|
||||
for BatchedFheUint8<M>
|
||||
where
|
||||
<M as Matrix>::R: RowMut,
|
||||
{
|
||||
fn from(value: &SeededBatchedFheUint8<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.seed);
|
||||
let rlwes = value
|
||||
.data
|
||||
.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 }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SeededBatchedFheUint8<C, S> {
|
||||
data: Vec<C>,
|
||||
seed: S,
|
||||
}
|
||||
|
||||
impl<C, S> SeededBatchedFheUint8<C, S> {
|
||||
pub fn unseed<M>(&self) -> BatchedFheUint8<M>
|
||||
where
|
||||
BatchedFheUint8<M>: for<'a> From<&'a SeededBatchedFheUint8<C, S>>,
|
||||
M: Matrix<R = C>,
|
||||
{
|
||||
BatchedFheUint8::from(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, C, S> Encryptor<[u8], SeededBatchedFheUint8<C, S>> for K
|
||||
where
|
||||
K: Encryptor<[bool], (Vec<C>, S)>,
|
||||
{
|
||||
fn encrypt(&self, m: &[u8]) -> SeededBatchedFheUint8<C, S> {
|
||||
// convert vector of u8s to vector bools
|
||||
let m = m
|
||||
.iter()
|
||||
.flat_map(|v| (0..8).into_iter().map(|i| (((*v) >> i) & 1) == 1))
|
||||
.collect_vec();
|
||||
let (cts, seed) = K::encrypt(&self, &m);
|
||||
dbg!(cts.len());
|
||||
SeededBatchedFheUint8 { data: cts, seed }
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, C> Encryptor<[u8], BatchedFheUint8<C>> for K
|
||||
where
|
||||
K: Encryptor<[bool], Vec<C>>,
|
||||
{
|
||||
fn encrypt(&self, m: &[u8]) -> BatchedFheUint8<C> {
|
||||
let m = m
|
||||
.iter()
|
||||
.flat_map(|v| {
|
||||
(0..8)
|
||||
.into_iter()
|
||||
.map(|i| ((*v >> i) & 1) == 1)
|
||||
.collect_vec()
|
||||
})
|
||||
.collect_vec();
|
||||
let cts = K::encrypt(&self, &m);
|
||||
BatchedFheUint8 { data: cts }
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> KeySwitchWithId<BatchedFheUint8<C>> for BatchedFheUint8<C>
|
||||
where
|
||||
C: KeySwitchWithId<C>,
|
||||
{
|
||||
fn key_switch(&self, user_id: usize) -> BatchedFheUint8<C> {
|
||||
let data = self
|
||||
.data
|
||||
.iter()
|
||||
.map(|c| c.key_switch(user_id))
|
||||
.collect_vec();
|
||||
BatchedFheUint8 { data }
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, K> MultiPartyDecryptor<u8, FheUint8<C>> for K
|
||||
where
|
||||
K: MultiPartyDecryptor<bool, C>,
|
||||
<Self as MultiPartyDecryptor<bool, C>>::DecryptionShare: Clone,
|
||||
{
|
||||
type DecryptionShare = Vec<<Self as MultiPartyDecryptor<bool, C>>::DecryptionShare>;
|
||||
fn gen_decryption_share(&self, c: &FheUint8<C>) -> Self::DecryptionShare {
|
||||
assert!(c.data().len() == 8);
|
||||
c.data()
|
||||
.iter()
|
||||
.map(|bit_c| {
|
||||
let decryption_share =
|
||||
MultiPartyDecryptor::<bool, C>::gen_decryption_share(self, bit_c);
|
||||
decryption_share
|
||||
})
|
||||
.collect_vec()
|
||||
}
|
||||
|
||||
fn aggregate_decryption_shares(&self, c: &FheUint8<C>, shares: &[Self::DecryptionShare]) -> u8 {
|
||||
let mut out = 0u8;
|
||||
|
||||
(0..8).into_iter().for_each(|i| {
|
||||
// Collect bit i^th decryption share of each party
|
||||
let bit_i_decryption_shares = shares.iter().map(|s| s[i].clone()).collect_vec();
|
||||
let bit_i = MultiPartyDecryptor::<bool, C>::aggregate_decryption_shares(
|
||||
self,
|
||||
&c.data()[i],
|
||||
&bit_i_decryption_shares,
|
||||
);
|
||||
|
||||
if bit_i {
|
||||
out += 1 << i;
|
||||
}
|
||||
});
|
||||
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, K> Encryptor<u8, FheUint8<C>> for K
|
||||
where
|
||||
K: Encryptor<bool, C>,
|
||||
{
|
||||
fn encrypt(&self, m: &u8) -> FheUint8<C> {
|
||||
let cts = (0..8)
|
||||
.into_iter()
|
||||
.map(|i| {
|
||||
let bit = ((m >> i) & 1) == 1;
|
||||
K::encrypt(self, &bit)
|
||||
})
|
||||
.collect_vec();
|
||||
FheUint8 { data: cts }
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, C> Decryptor<u8, FheUint8<C>> for K
|
||||
where
|
||||
K: Decryptor<bool, C>,
|
||||
{
|
||||
fn decrypt(&self, c: &FheUint8<C>) -> u8 {
|
||||
assert!(c.data.len() == 8);
|
||||
let mut out = 0u8;
|
||||
c.data().iter().enumerate().for_each(|(index, bit_c)| {
|
||||
let bool = K::decrypt(self, bit_c);
|
||||
if bool {
|
||||
out += 1 << index;
|
||||
}
|
||||
});
|
||||
out
|
||||
}
|
||||
}
|
||||
@@ -1,110 +1,30 @@
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::{
|
||||
bool::{ClientKey, PublicKey},
|
||||
Decryptor, Encryptor, MultiPartyDecryptor,
|
||||
bool::{parameters::CiphertextModulus, ClientKey, PublicKey},
|
||||
random::{DefaultSecureRng, NewWithSeed, RandomFillUniformInModulus},
|
||||
utils::{TryConvertFrom1, WithLocal},
|
||||
Decryptor, Encryptor, Matrix, MatrixEntity, MatrixMut, MultiPartyDecryptor, Row, RowMut,
|
||||
};
|
||||
|
||||
mod enc_dec;
|
||||
mod ops;
|
||||
mod types;
|
||||
|
||||
pub type FheUint8 = types::FheUint8<Vec<u64>>;
|
||||
|
||||
impl Encryptor<u8, FheUint8> for ClientKey {
|
||||
fn encrypt(&self, m: &u8) -> FheUint8 {
|
||||
let cts = (0..8)
|
||||
.into_iter()
|
||||
.map(|i| {
|
||||
let bit = ((m >> i) & 1) == 1;
|
||||
Encryptor::<bool, Vec<u64>>::encrypt(self, &bit)
|
||||
})
|
||||
.collect_vec();
|
||||
FheUint8 { data: cts }
|
||||
}
|
||||
}
|
||||
|
||||
impl Decryptor<u8, FheUint8> for ClientKey {
|
||||
fn decrypt(&self, c: &FheUint8) -> u8 {
|
||||
assert!(c.data.len() == 8);
|
||||
let mut out = 0u8;
|
||||
c.data().iter().enumerate().for_each(|(index, bit_c)| {
|
||||
let bool = Decryptor::<bool, Vec<u64>>::decrypt(self, bit_c);
|
||||
if bool {
|
||||
out += 1 << index;
|
||||
}
|
||||
});
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
impl<M, R, Mo> Encryptor<u8, FheUint8> for PublicKey<M, R, Mo>
|
||||
where
|
||||
PublicKey<M, R, Mo>: Encryptor<bool, Vec<u64>>,
|
||||
{
|
||||
fn encrypt(&self, m: &u8) -> FheUint8 {
|
||||
let cts = (0..8)
|
||||
.into_iter()
|
||||
.map(|i| {
|
||||
let bit = ((m >> i) & 1) == 1;
|
||||
Encryptor::<bool, Vec<u64>>::encrypt(self, &bit)
|
||||
})
|
||||
.collect_vec();
|
||||
FheUint8 { data: cts }
|
||||
}
|
||||
}
|
||||
|
||||
impl MultiPartyDecryptor<u8, FheUint8> for ClientKey
|
||||
where
|
||||
ClientKey: MultiPartyDecryptor<bool, Vec<u64>>,
|
||||
{
|
||||
type DecryptionShare = Vec<<Self as MultiPartyDecryptor<bool, Vec<u64>>>::DecryptionShare>;
|
||||
fn gen_decryption_share(&self, c: &FheUint8) -> Self::DecryptionShare {
|
||||
assert!(c.data().len() == 8);
|
||||
c.data()
|
||||
.iter()
|
||||
.map(|bit_c| {
|
||||
let decryption_share =
|
||||
MultiPartyDecryptor::<bool, Vec<u64>>::gen_decryption_share(self, bit_c);
|
||||
decryption_share
|
||||
})
|
||||
.collect_vec()
|
||||
}
|
||||
|
||||
fn aggregate_decryption_shares(&self, c: &FheUint8, shares: &[Self::DecryptionShare]) -> u8 {
|
||||
let mut out = 0u8;
|
||||
|
||||
(0..8).into_iter().for_each(|i| {
|
||||
// Collect bit i^th decryption share of each party
|
||||
let bit_i_decryption_shares = shares.iter().map(|s| s[i]).collect_vec();
|
||||
let bit_i = MultiPartyDecryptor::<bool, Vec<u64>>::aggregate_decryption_shares(
|
||||
self,
|
||||
&c.data()[i],
|
||||
&bit_i_decryption_shares,
|
||||
);
|
||||
|
||||
if bit_i {
|
||||
out += 1 << i;
|
||||
}
|
||||
});
|
||||
|
||||
out
|
||||
}
|
||||
}
|
||||
pub type FheUint8 = enc_dec::FheUint8<Vec<u64>>;
|
||||
pub type FheBool = Vec<u64>;
|
||||
|
||||
mod frontend {
|
||||
use super::ops::{
|
||||
arbitrary_bit_adder, arbitrary_bit_division_for_quotient_and_rem, arbitrary_bit_subtractor,
|
||||
eight_bit_mul,
|
||||
};
|
||||
use crate::{
|
||||
bool::evaluator::{BoolEvaluator, BooleanGates},
|
||||
utils::{Global, WithLocal},
|
||||
};
|
||||
use crate::utils::{Global, WithLocal};
|
||||
|
||||
use super::FheUint8;
|
||||
use super::*;
|
||||
|
||||
mod arithetic {
|
||||
use crate::bool::{FheBool, RuntimeServerKey};
|
||||
use crate::bool::{evaluator::BooleanGates, BoolEvaluator, RuntimeServerKey};
|
||||
|
||||
use super::*;
|
||||
use std::ops::{Add, AddAssign, Div, Mul, Rem, Sub};
|
||||
@@ -230,7 +150,7 @@ mod frontend {
|
||||
|
||||
mod booleans {
|
||||
use crate::{
|
||||
bool::{evaluator::BooleanGates, FheBool, RuntimeServerKey},
|
||||
bool::{evaluator::BooleanGates, BoolEvaluator, RuntimeServerKey},
|
||||
shortint::ops::{
|
||||
arbitrary_bit_comparator, arbitrary_bit_equality, arbitrary_signed_bit_comparator,
|
||||
},
|
||||
@@ -303,214 +223,155 @@ mod tests {
|
||||
use num_traits::Euclid;
|
||||
|
||||
use crate::{
|
||||
bool::{
|
||||
aggregate_public_key_shares, aggregate_server_key_shares, gen_client_key, gen_keys,
|
||||
gen_mp_keys_phase1, gen_mp_keys_phase2, set_mp_seed, set_parameter_set,
|
||||
},
|
||||
shortint::types::FheUint8,
|
||||
Decryptor, Encryptor, MultiPartyDecryptor,
|
||||
bool::set_parameter_set, shortint::enc_dec::FheUint8, Decryptor, Encryptor,
|
||||
MultiPartyDecryptor,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn all_uint8_apis() {
|
||||
set_parameter_set(crate::ParameterSelector::MultiPartyLessThanOrEqualTo16);
|
||||
// #[test]
|
||||
// fn all_uint8_apis() {
|
||||
// set_parameter_set(crate::ParameterSelector::MultiPartyLessThanOrEqualTo16);
|
||||
|
||||
let (ck, sk) = gen_keys();
|
||||
sk.set_server_key();
|
||||
// let (ck, sk) = gen_keys();
|
||||
// sk.set_server_key();
|
||||
|
||||
for i in 144..=255 {
|
||||
for j in 100..=255 {
|
||||
let m0 = i;
|
||||
let m1 = j;
|
||||
let c0 = ck.encrypt(&m0);
|
||||
let c1 = ck.encrypt(&m1);
|
||||
// for i in 144..=255 {
|
||||
// for j in 100..=255 {
|
||||
// let m0 = i;
|
||||
// let m1 = j;
|
||||
// let c0 = ck.encrypt(&m0);
|
||||
// let c1 = ck.encrypt(&m1);
|
||||
|
||||
assert!(ck.decrypt(&c0) == m0);
|
||||
assert!(ck.decrypt(&c1) == m1);
|
||||
// assert!(ck.decrypt(&c0) == m0);
|
||||
// assert!(ck.decrypt(&c1) == m1);
|
||||
|
||||
// Arithmetic
|
||||
{
|
||||
{
|
||||
// Add
|
||||
let mut c_m0_plus_m1 = FheUint8 {
|
||||
data: c0.data().to_vec(),
|
||||
};
|
||||
c_m0_plus_m1 += &c1;
|
||||
let m0_plus_m1 = ck.decrypt(&c_m0_plus_m1);
|
||||
assert_eq!(
|
||||
m0_plus_m1,
|
||||
m0.wrapping_add(m1),
|
||||
"Expected {} but got {m0_plus_m1} for {i}+{j}",
|
||||
m0.wrapping_add(m1)
|
||||
);
|
||||
}
|
||||
{
|
||||
// Sub
|
||||
let c_sub = &c0 - &c1;
|
||||
let m0_sub_m1 = ck.decrypt(&c_sub);
|
||||
assert_eq!(
|
||||
m0_sub_m1,
|
||||
m0.wrapping_sub(m1),
|
||||
"Expected {} but got {m0_sub_m1} for {i}-{j}",
|
||||
m0.wrapping_sub(m1)
|
||||
);
|
||||
}
|
||||
// // Arithmetic
|
||||
// {
|
||||
// {
|
||||
// // Add
|
||||
// let mut c_m0_plus_m1 = FheUint8 {
|
||||
// data: c0.data().to_vec(),
|
||||
// };
|
||||
// c_m0_plus_m1 += &c1;
|
||||
// let m0_plus_m1 = ck.decrypt(&c_m0_plus_m1);
|
||||
// assert_eq!(
|
||||
// m0_plus_m1,
|
||||
// m0.wrapping_add(m1),
|
||||
// "Expected {} but got {m0_plus_m1} for {i}+{j}",
|
||||
// m0.wrapping_add(m1)
|
||||
// );
|
||||
// }
|
||||
// {
|
||||
// // Sub
|
||||
// let c_sub = &c0 - &c1;
|
||||
// let m0_sub_m1 = ck.decrypt(&c_sub);
|
||||
// assert_eq!(
|
||||
// m0_sub_m1,
|
||||
// m0.wrapping_sub(m1),
|
||||
// "Expected {} but got {m0_sub_m1} for {i}-{j}",
|
||||
// m0.wrapping_sub(m1)
|
||||
// );
|
||||
// }
|
||||
|
||||
{
|
||||
// Mul
|
||||
let c_m0m1 = &c0 * &c1;
|
||||
let m0m1 = ck.decrypt(&c_m0m1);
|
||||
assert_eq!(
|
||||
m0m1,
|
||||
m0.wrapping_mul(m1),
|
||||
"Expected {} but got {m0m1} for {i}x{j}",
|
||||
m0.wrapping_mul(m1)
|
||||
);
|
||||
}
|
||||
// {
|
||||
// // Mul
|
||||
// let c_m0m1 = &c0 * &c1;
|
||||
// let m0m1 = ck.decrypt(&c_m0m1);
|
||||
// assert_eq!(
|
||||
// m0m1,
|
||||
// m0.wrapping_mul(m1),
|
||||
// "Expected {} but got {m0m1} for {i}x{j}",
|
||||
// m0.wrapping_mul(m1)
|
||||
// );
|
||||
// }
|
||||
|
||||
// Div & Rem
|
||||
{
|
||||
let (c_quotient, c_rem) = c0.div_rem(&c1);
|
||||
let m_quotient = ck.decrypt(&c_quotient);
|
||||
let m_remainder = ck.decrypt(&c_rem);
|
||||
if j != 0 {
|
||||
let (q, r) = i.div_rem_euclid(&j);
|
||||
assert_eq!(
|
||||
m_quotient, q,
|
||||
"Expected {} but got {m_quotient} for {i}/{j}",
|
||||
q
|
||||
);
|
||||
assert_eq!(
|
||||
m_remainder, r,
|
||||
"Expected {} but got {m_quotient} for {i}%{j}",
|
||||
r
|
||||
);
|
||||
} else {
|
||||
assert_eq!(
|
||||
m_quotient, 255,
|
||||
"Expected 255 but got {m_quotient}. Case div by zero"
|
||||
);
|
||||
assert_eq!(
|
||||
m_remainder, i,
|
||||
"Expected {i} but got {m_quotient}. Case div by zero"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
// // Div & Rem
|
||||
// {
|
||||
// let (c_quotient, c_rem) = c0.div_rem(&c1);
|
||||
// let m_quotient = ck.decrypt(&c_quotient);
|
||||
// let m_remainder = ck.decrypt(&c_rem);
|
||||
// if j != 0 {
|
||||
// let (q, r) = i.div_rem_euclid(&j);
|
||||
// assert_eq!(
|
||||
// m_quotient, q,
|
||||
// "Expected {} but got {m_quotient} for
|
||||
// {i}/{j}", q
|
||||
// );
|
||||
// assert_eq!(
|
||||
// m_remainder, r,
|
||||
// "Expected {} but got {m_quotient} for
|
||||
// {i}%{j}", r
|
||||
// );
|
||||
// } else {
|
||||
// assert_eq!(
|
||||
// m_quotient, 255,
|
||||
// "Expected 255 but got {m_quotient}. Case div
|
||||
// by zero" );
|
||||
// assert_eq!(
|
||||
// m_remainder, i,
|
||||
// "Expected {i} but got {m_quotient}. Case div
|
||||
// by zero" )
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// Comparisons
|
||||
{
|
||||
{
|
||||
let c_eq = c0.eq(&c1);
|
||||
let is_eq = ck.decrypt(&c_eq);
|
||||
assert_eq!(
|
||||
is_eq,
|
||||
i == j,
|
||||
"Expected {} but got {is_eq} for {i}=={j}",
|
||||
i == j
|
||||
);
|
||||
}
|
||||
// // Comparisons
|
||||
// {
|
||||
// {
|
||||
// let c_eq = c0.eq(&c1);
|
||||
// let is_eq = ck.decrypt(&c_eq);
|
||||
// assert_eq!(
|
||||
// is_eq,
|
||||
// i == j,
|
||||
// "Expected {} but got {is_eq} for {i}=={j}",
|
||||
// i == j
|
||||
// );
|
||||
// }
|
||||
|
||||
{
|
||||
let c_gt = c0.gt(&c1);
|
||||
let is_gt = ck.decrypt(&c_gt);
|
||||
assert_eq!(
|
||||
is_gt,
|
||||
i > j,
|
||||
"Expected {} but got {is_gt} for {i}>{j}",
|
||||
i > j
|
||||
);
|
||||
}
|
||||
// {
|
||||
// let c_gt = c0.gt(&c1);
|
||||
// let is_gt = ck.decrypt(&c_gt);
|
||||
// assert_eq!(
|
||||
// is_gt,
|
||||
// i > j,
|
||||
// "Expected {} but got {is_gt} for {i}>{j}",
|
||||
// i > j
|
||||
// );
|
||||
// }
|
||||
|
||||
{
|
||||
let c_lt = c0.lt(&c1);
|
||||
let is_lt = ck.decrypt(&c_lt);
|
||||
assert_eq!(
|
||||
is_lt,
|
||||
i < j,
|
||||
"Expected {} but got {is_lt} for {i}<{j}",
|
||||
i < j
|
||||
);
|
||||
}
|
||||
// {
|
||||
// let c_lt = c0.lt(&c1);
|
||||
// let is_lt = ck.decrypt(&c_lt);
|
||||
// assert_eq!(
|
||||
// is_lt,
|
||||
// i < j,
|
||||
// "Expected {} but got {is_lt} for {i}<{j}",
|
||||
// i < j
|
||||
// );
|
||||
// }
|
||||
|
||||
{
|
||||
let c_ge = c0.ge(&c1);
|
||||
let is_ge = ck.decrypt(&c_ge);
|
||||
assert_eq!(
|
||||
is_ge,
|
||||
i >= j,
|
||||
"Expected {} but got {is_ge} for {i}>={j}",
|
||||
i >= j
|
||||
);
|
||||
}
|
||||
// {
|
||||
// let c_ge = c0.ge(&c1);
|
||||
// let is_ge = ck.decrypt(&c_ge);
|
||||
// assert_eq!(
|
||||
// is_ge,
|
||||
// i >= j,
|
||||
// "Expected {} but got {is_ge} for {i}>={j}",
|
||||
// i >= j
|
||||
// );
|
||||
// }
|
||||
|
||||
{
|
||||
let c_le = c0.le(&c1);
|
||||
let is_le = ck.decrypt(&c_le);
|
||||
assert_eq!(
|
||||
is_le,
|
||||
i <= j,
|
||||
"Expected {} but got {is_le} for {i}<={j}",
|
||||
i <= j
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fheuint8_test_multi_party() {
|
||||
set_parameter_set(crate::ParameterSelector::MultiPartyLessThanOrEqualTo16);
|
||||
set_mp_seed([0; 32]);
|
||||
|
||||
let parties = 8;
|
||||
|
||||
// client keys and public key share
|
||||
let cks = (0..parties)
|
||||
.into_iter()
|
||||
.map(|i| gen_client_key())
|
||||
.collect_vec();
|
||||
|
||||
// round 1: generate pulic key shares
|
||||
let pk_shares = cks.iter().map(|key| gen_mp_keys_phase1(key)).collect_vec();
|
||||
|
||||
let public_key = aggregate_public_key_shares(&pk_shares);
|
||||
|
||||
// round 2: generate server key shares
|
||||
let server_key_shares = cks
|
||||
.iter()
|
||||
.map(|key| gen_mp_keys_phase2(key, &public_key))
|
||||
.collect_vec();
|
||||
|
||||
// server aggregates the server key
|
||||
let server_key = aggregate_server_key_shares(&server_key_shares);
|
||||
server_key.set_server_key();
|
||||
|
||||
// Clients use Pk to encrypt private inputs
|
||||
let a = 8u8;
|
||||
let b = 10u8;
|
||||
let c = 155u8;
|
||||
let ct_a = public_key.encrypt(&a);
|
||||
let ct_b = public_key.encrypt(&b);
|
||||
let ct_c = public_key.encrypt(&c);
|
||||
|
||||
let now = std::time::Instant::now();
|
||||
// server computes
|
||||
// a*b + c
|
||||
let mut ct_ab = &ct_a * &ct_b;
|
||||
ct_ab += &ct_c;
|
||||
println!("Circuit time: {:?}", now.elapsed());
|
||||
|
||||
// decrypt ab and check
|
||||
// generate decryption shares
|
||||
let decryption_shares = cks
|
||||
.iter()
|
||||
.map(|k| k.gen_decryption_share(&ct_ab))
|
||||
.collect_vec();
|
||||
|
||||
// aggregate and decryption ab
|
||||
let ab_add_c = cks[0].aggregate_decryption_shares(&ct_ab, &decryption_shares);
|
||||
assert!(ab_add_c == (a.wrapping_mul(b)).wrapping_add(c));
|
||||
}
|
||||
// {
|
||||
// let c_le = c0.le(&c1);
|
||||
// let is_le = ck.decrypt(&c_le);
|
||||
// assert_eq!(
|
||||
// is_le,
|
||||
// i <= j,
|
||||
// "Expected {} but got {is_le} for {i}<={j}",
|
||||
// i <= j
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
#[derive(Clone)]
|
||||
pub struct FheUint8<C> {
|
||||
pub(super) data: Vec<C>,
|
||||
}
|
||||
|
||||
impl<C> FheUint8<C> {
|
||||
pub(super) fn data(&self) -> &[C] {
|
||||
&self.data
|
||||
}
|
||||
|
||||
pub(super) fn data_mut(&mut self) -> &mut [C] {
|
||||
&mut self.data
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user