arnaucube
arnaucube/phantom-zonePublic Self-hosted repository
phantom-zone/src/bool/ni_mp_api.rs
src/bool/ni_mp_api.rsRaw
  1use std::{cell::RefCell, sync::OnceLock};
  2
  3use crate::{
  4    backend::ModulusPowerOf2,
  5    bool::parameters::ParameterVariant,
  6    random::DefaultSecureRng,
  7    utils::{Global, WithLocal},
  8    ModularOpsU64, NttBackendU64,
  9};
 10
 11use super::{
 12    evaluator::NonInteractiveMultiPartyCrs,
 13    keys::{
 14        CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare,
 15        NonInteractiveServerKeyEvaluationDomain, SeededNonInteractiveMultiPartyServerKey,
 16        ShoupNonInteractiveServerKeyEvaluationDomain,
 17    },
 18    parameters::{BoolParameters, CiphertextModulus, NI_2P},
 19    ClientKey, ParameterSelector,
 20};
 21
 22pub(crate) type BoolEvaluator = super::evaluator::BoolEvaluator<
 23    Vec<Vec<u64>>,
 24    NttBackendU64,
 25    ModularOpsU64<CiphertextModulus<u64>>,
 26    ModulusPowerOf2<CiphertextModulus<u64>>,
 27    ShoupNonInteractiveServerKeyEvaluationDomain<Vec<Vec<u64>>>,
 28>;
 29
 30thread_local! {
 31    static BOOL_EVALUATOR: RefCell<Option<BoolEvaluator>> = RefCell::new(None);
 32
 33}
 34static BOOL_SERVER_KEY: OnceLock<ShoupNonInteractiveServerKeyEvaluationDomain<Vec<Vec<u64>>>> =
 35    OnceLock::new();
 36
 37static MULTI_PARTY_CRS: OnceLock<NonInteractiveMultiPartyCrs<[u8; 32]>> = OnceLock::new();
 38
 39pub fn set_parameter_set(select: ParameterSelector) {
 40    match select {
 41        ParameterSelector::NonInteractiveMultiPartyLessThanOrEqualTo16 => {
 42            BOOL_EVALUATOR.with_borrow_mut(|v| *v = Some(BoolEvaluator::new(NI_2P)));
 43        }
 44        _ => {
 45            panic!("Paramerters not supported")
 46        }
 47    }
 48}
 49
 50pub fn set_common_reference_seed(seed: [u8; 32]) {
 51    BoolEvaluator::with_local(|e| {
 52        assert_eq!(
 53            e.parameters().variant(),
 54            &ParameterVariant::NonInteractiveMultiParty,
 55            "Set parameters do not support Non interactive multi-party"
 56        );
 57    });
 58
 59    assert!(
 60        MULTI_PARTY_CRS
 61            .set(NonInteractiveMultiPartyCrs { seed: seed })
 62            .is_ok(),
 63        "Attempted to set MP SEED twice."
 64    )
 65}
 66
 67pub fn gen_client_key() -> ClientKey {
 68    BoolEvaluator::with_local(|e| e.client_key())
 69}
 70
 71pub fn gen_server_key_share(
 72    user_id: usize,
 73    total_users: usize,
 74    client_key: &ClientKey,
 75) -> CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<
 76    Vec<Vec<u64>>,
 77    NonInteractiveMultiPartyCrs<[u8; 32]>,
 78> {
 79    BoolEvaluator::with_local(|e| {
 80        let cr_seed = NonInteractiveMultiPartyCrs::global();
 81        e.non_interactive_multi_party_key_share(cr_seed, user_id, total_users, client_key)
 82    })
 83}
 84
 85pub fn aggregate_server_key_shares(
 86    shares: &[CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<
 87        Vec<Vec<u64>>,
 88        NonInteractiveMultiPartyCrs<[u8; 32]>,
 89    >],
 90) -> SeededNonInteractiveMultiPartyServerKey<
 91    Vec<Vec<u64>>,
 92    NonInteractiveMultiPartyCrs<[u8; 32]>,
 93    BoolParameters<u64>,
 94> {
 95    BoolEvaluator::with_local(|e| {
 96        let cr_seed = NonInteractiveMultiPartyCrs::global();
 97        e.aggregate_non_interactive_multi_party_key_share(cr_seed, shares.len(), shares)
 98    })
 99}
100
101impl
102    SeededNonInteractiveMultiPartyServerKey<
103        Vec<Vec<u64>>,
104        NonInteractiveMultiPartyCrs<[u8; 32]>,
105        BoolParameters<u64>,
106    >
107{
108    pub fn set_server_key(&self) {
109        let eval_key = NonInteractiveServerKeyEvaluationDomain::<
110            _,
111            BoolParameters<u64>,
112            DefaultSecureRng,
113            NttBackendU64,
114        >::from(self);
115        assert!(
116            BOOL_SERVER_KEY
117                .set(ShoupNonInteractiveServerKeyEvaluationDomain::from(eval_key))
118                .is_ok(),
119            "Attempted to set server key twice!"
120        );
121    }
122}
123
124impl Global for NonInteractiveMultiPartyCrs<[u8; 32]> {
125    fn global() -> &'static Self {
126        MULTI_PARTY_CRS
127            .get()
128            .expect("Non-interactive multi-party common reference string not set")
129    }
130}
131
132// BOOL EVALUATOR //
133impl WithLocal for BoolEvaluator {
134    fn with_local<F, R>(func: F) -> R
135    where
136        F: Fn(&Self) -> R,
137    {
138        BOOL_EVALUATOR.with_borrow(|s| func(s.as_ref().expect("Parameters not set")))
139    }
140
141    fn with_local_mut<F, R>(func: F) -> R
142    where
143        F: Fn(&mut Self) -> R,
144    {
145        BOOL_EVALUATOR.with_borrow_mut(|s| func(s.as_mut().expect("Parameters not set")))
146    }
147
148    fn with_local_mut_mut<F, R>(func: &mut F) -> R
149    where
150        F: FnMut(&mut Self) -> R,
151    {
152        BOOL_EVALUATOR.with_borrow_mut(|s| func(s.as_mut().expect("Parameters not set")))
153    }
154}
155
156pub(crate) type RuntimeServerKey = ShoupNonInteractiveServerKeyEvaluationDomain<Vec<Vec<u64>>>;
157impl Global for RuntimeServerKey {
158    fn global() -> &'static Self {
159        BOOL_SERVER_KEY.get().expect("Server key not set!")
160    }
161}
162
163/// Non interactive multi-party specfic encryptor decryptor routines
164mod impl_enc_dec {
165    use crate::{
166        bool::{evaluator::BoolEncoding, keys::NonInteractiveMultiPartyClientKey},
167        pbs::{sample_extract, PbsInfo, WithShoupRepr},
168        random::{NewWithSeed, RandomFillUniformInModulus},
169        rgsw::{key_switch, secret_key_encrypt_rlwe},
170        utils::TryConvertFrom1,
171        Encryptor, KeySwitchWithId, Matrix, MatrixEntity, MatrixMut, RowEntity, RowMut,
172    };
173    use itertools::Itertools;
174    use num_traits::{ToPrimitive, Zero};
175
176    use super::*;
177
178    type Mat = Vec<Vec<u64>>;
179
180    pub(super) struct BatchedFheBools<C> {
181        pub(super) data: Vec<C>,
182    }
183
184    impl<C: MatrixMut<MatElement = u64>> BatchedFheBools<C>
185    where
186        C::R: RowEntity + RowMut,
187    {
188        pub(super) fn extract(&self, index: usize) -> C::R {
189            BoolEvaluator::with_local(|e| {
190                let ring_size = e.parameters().rlwe_n().0;
191                let ct_index = index / ring_size;
192                let coeff_index = index % ring_size;
193                let mut lwe_out = C::R::zeros(e.parameters().rlwe_n().0 + 1);
194                sample_extract(
195                    &mut lwe_out,
196                    &self.data[ct_index],
197                    e.pbs_info().modop_rlweq(),
198                    coeff_index,
199                );
200                lwe_out
201            })
202        }
203    }
204
205    pub(super) struct NonInteractiveBatchedFheBools<C> {
206        data: Vec<C>,
207    }
208
209    impl<M: MatrixEntity + MatrixMut<MatElement = u64>> From<&(Vec<M::R>, [u8; 32])>
210        for NonInteractiveBatchedFheBools<M>
211    where
212        <M as Matrix>::R: RowMut,
213    {
214        fn from(value: &(Vec<M::R>, [u8; 32])) -> Self {
215            BoolEvaluator::with_local(|e| {
216                let parameters = e.parameters();
217                let ring_size = parameters.rlwe_n().0;
218                let rlwe_q = parameters.rlwe_q();
219
220                let mut prng = DefaultSecureRng::new_seeded(value.1);
221                let rlwes = value
222                    .0
223                    .iter()
224                    .map(|partb| {
225                        let mut rlwe = M::zeros(2, ring_size);
226
227                        // sample A
228                        RandomFillUniformInModulus::random_fill(
229                            &mut prng,
230                            rlwe_q,
231                            rlwe.get_row_mut(0),
232                        );
233
234                        // Copy over B
235                        rlwe.get_row_mut(1).copy_from_slice(partb.as_ref());
236
237                        rlwe
238                    })
239                    .collect_vec();
240                Self { data: rlwes }
241            })
242        }
243    }
244
245    impl<K> Encryptor<[bool], NonInteractiveBatchedFheBools<Mat>> for K
246    where
247        K: Encryptor<[bool], (Mat, [u8; 32])>,
248    {
249        fn encrypt(&self, m: &[bool]) -> NonInteractiveBatchedFheBools<Mat> {
250            NonInteractiveBatchedFheBools::from(&K::encrypt(&self, m))
251        }
252    }
253
254    impl<K> Encryptor<[bool], (Mat, [u8; 32])> for K
255    where
256        K: NonInteractiveMultiPartyClientKey,
257        <Mat as Matrix>::R:
258            TryConvertFrom1<[K::Element], CiphertextModulus<<Mat as Matrix>::MatElement>>,
259    {
260        fn encrypt(&self, m: &[bool]) -> (Mat, [u8; 32]) {
261            BoolEvaluator::with_local(|e| {
262                DefaultSecureRng::with_local_mut(|rng| {
263                    let parameters = e.parameters();
264                    let ring_size = parameters.rlwe_n().0;
265
266                    let rlwe_count = ((m.len() as f64 / ring_size as f64).ceil())
267                        .to_usize()
268                        .unwrap();
269
270                    let mut seed = <DefaultSecureRng as NewWithSeed>::Seed::default();
271                    rng.fill_bytes(&mut seed);
272                    let mut prng = DefaultSecureRng::new_seeded(seed);
273
274                    let sk_u = self.sk_u_rlwe();
275
276                    // encrypt `m` into ceil(len(m)/N) RLWE ciphertexts
277                    let rlwes = (0..rlwe_count)
278                        .map(|index| {
279                            let mut message = vec![<Mat as Matrix>::MatElement::zero(); ring_size];
280                            m[(index * ring_size)..std::cmp::min(m.len(), (index + 1) * ring_size)]
281                                .iter()
282                                .enumerate()
283                                .for_each(|(i, v)| {
284                                    if *v {
285                                        message[i] = parameters.rlwe_q().true_el()
286                                    } else {
287                                        message[i] = parameters.rlwe_q().false_el()
288                                    }
289                                });
290
291                            // encrypt message
292                            let mut rlwe_out =
293                                <<Mat as Matrix>::R as RowEntity>::zeros(parameters.rlwe_n().0);
294
295                            secret_key_encrypt_rlwe(
296                                &message,
297                                &mut rlwe_out,
298                                &sk_u,
299                                e.pbs_info().modop_rlweq(),
300                                e.pbs_info().nttop_rlweq(),
301                                &mut prng,
302                                rng,
303                            );
304
305                            rlwe_out
306                        })
307                        .collect_vec();
308
309                    (rlwes, seed)
310                })
311            })
312        }
313    }
314
315    impl KeySwitchWithId<Mat> for Mat {
316        fn key_switch(&self, user_id: usize) -> Mat {
317            BoolEvaluator::with_local(|e| {
318                let server_key = BOOL_SERVER_KEY.get().unwrap();
319                let ksk = server_key.ui_to_s_ksk(user_id);
320                let decomposer = e.ni_ui_to_s_ks_decomposer().as_ref().unwrap();
321
322                // perform key switch
323                key_switch(
324                    self,
325                    ksk.as_ref(),
326                    ksk.shoup_repr(),
327                    decomposer,
328                    e.pbs_info().nttop_rlweq(),
329                    e.pbs_info().modop_rlweq(),
330                )
331            })
332        }
333    }
334
335    impl<C> KeySwitchWithId<BatchedFheBools<C>> for NonInteractiveBatchedFheBools<C>
336    where
337        C: KeySwitchWithId<C>,
338    {
339        fn key_switch(&self, user_id: usize) -> BatchedFheBools<C> {
340            let data = self
341                .data
342                .iter()
343                .map(|c| c.key_switch(user_id))
344                .collect_vec();
345            BatchedFheBools { data }
346        }
347    }
348}
349
350#[cfg(test)]
351mod tests {
352    use impl_enc_dec::NonInteractiveBatchedFheBools;
353    use itertools::{izip, Itertools};
354    use num_traits::{FromPrimitive, PrimInt, ToPrimitive, Zero};
355    use rand::{thread_rng, RngCore};
356
357    use crate::{
358        backend::{GetModulus, Modulus},
359        bool::{
360            evaluator::BooleanGates,
361            keys::{
362                tests::{ideal_sk_rlwe, measure_noise_lwe},
363                SinglePartyClientKey,
364            },
365        },
366        lwe::decrypt_lwe,
367        rgsw::decrypt_rlwe,
368        utils::{tests::Stats, TryConvertFrom1},
369        ArithmeticOps, Encoder, Encryptor, KeySwitchWithId, ModInit, MultiPartyDecryptor, NttInit,
370        Row, VectorOps,
371    };
372
373    use super::*;
374
375    #[test]
376    fn non_interactive_mp_bool_nand() {
377        set_parameter_set(ParameterSelector::NonInteractiveMultiPartyLessThanOrEqualTo16);
378        let mut seed = [0u8; 32];
379        thread_rng().fill_bytes(&mut seed);
380        set_common_reference_seed(seed);
381
382        let parties = 2;
383
384        let cks = (0..parties).map(|_| gen_client_key()).collect_vec();
385
386        let key_shares = cks
387            .iter()
388            .enumerate()
389            .map(|(user_index, ck)| gen_server_key_share(user_index, parties, ck))
390            .collect_vec();
391
392        let seeded_server_key = aggregate_server_key_shares(&key_shares);
393        seeded_server_key.set_server_key();
394
395        let parameters = BoolEvaluator::with_local(|e| e.parameters().clone());
396        let rlwe_modop = parameters.default_rlwe_modop();
397
398        let ideal_sk_rlwe = ideal_sk_rlwe(&cks);
399
400        let mut m0 = false;
401        let mut m1 = true;
402
403        let mut ct0 = {
404            let ct: NonInteractiveBatchedFheBools<_> = cks[0].encrypt(vec![m0].as_slice());
405            let ct = ct.key_switch(0);
406            ct.extract(0)
407        };
408        let mut ct1 = {
409            let ct: NonInteractiveBatchedFheBools<_> = cks[1].encrypt(vec![m1].as_slice());
410            let ct = ct.key_switch(1);
411            ct.extract(0)
412        };
413
414        for _ in 0..1000 {
415            // let now = std::time::Instant::now();
416            let ct_out =
417                BoolEvaluator::with_local_mut(|e| e.xor(&ct0, &ct1, RuntimeServerKey::global()));
418            // println!("Time: {:?}", now.elapsed());
419
420            let decryption_shares = cks
421                .iter()
422                .map(|k| k.gen_decryption_share(&ct_out))
423                .collect_vec();
424            let m_out = cks[0].aggregate_decryption_shares(&ct_out, &decryption_shares);
425
426            let m_expected = m0 ^ m1;
427
428            {
429                let noise = measure_noise_lwe(
430                    &ct_out,
431                    parameters.rlwe_q().encode(m_expected),
432                    &ideal_sk_rlwe,
433                    &rlwe_modop,
434                );
435                println!("Noise: {noise}");
436            }
437
438            assert!(m_out == m_expected, "Expected {m_expected} but got {m_out}");
439
440            m1 = m0;
441            m0 = m_out;
442
443            ct1 = ct0;
444            ct0 = ct_out;
445        }
446    }
447
448    #[test]
449    fn trialtest() {
450        set_parameter_set(ParameterSelector::NonInteractiveMultiPartyLessThanOrEqualTo16);
451        set_common_reference_seed([2; 32]);
452
453        let parties = 2;
454
455        let cks = (0..parties).map(|_| gen_client_key()).collect_vec();
456
457        let key_shares = cks
458            .iter()
459            .enumerate()
460            .map(|(user_index, ck)| gen_server_key_share(user_index, parties, ck))
461            .collect_vec();
462
463        let seeded_server_key = aggregate_server_key_shares(&key_shares);
464        seeded_server_key.set_server_key();
465
466        let m = vec![false, true];
467        let ct: NonInteractiveBatchedFheBools<_> = cks[0].encrypt(m.as_slice());
468        let ct = ct.key_switch(0);
469
470        let parameters = BoolEvaluator::with_local(|e| e.parameters().clone());
471        let nttop = NttBackendU64::new(parameters.rlwe_q(), parameters.rlwe_n().0);
472        let rlwe_q_modop = ModularOpsU64::new(*parameters.rlwe_q());
473
474        let mut ideal_rlwe_sk = vec![0i32; parameters.rlwe_n().0];
475        cks.iter().for_each(|k| {
476            let sk_rlwe = k.sk_rlwe();
477            izip!(ideal_rlwe_sk.iter_mut(), sk_rlwe.iter()).for_each(|(a, b)| {
478                *a = *a + b;
479            });
480        });
481
482        let message = m
483            .iter()
484            .map(|b| parameters.rlwe_q().encode(*b))
485            .collect_vec();
486
487        let mut m_out = vec![0u64; parameters.rlwe_n().0];
488        decrypt_rlwe(
489            &ct.data[0],
490            &ideal_rlwe_sk,
491            &mut m_out,
492            &nttop,
493            &rlwe_q_modop,
494        );
495
496        let mut diff = m_out;
497        rlwe_q_modop.elwise_sub_mut(diff.as_mut_slice(), message.as_ref());
498
499        let mut stats = Stats::new();
500        stats.add_more(&Vec::<i64>::try_convert_from(
501            diff.as_slice(),
502            parameters.rlwe_q(),
503        ));
504        println!("Noise: {}", stats.std_dev().abs().log2());
505    }
506}