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