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::{DefaultSecureRng, NewWithSeed, RandomFillUniformInModulus},
171        rgsw::{key_switch, secret_key_encrypt_rlwe},
172        utils::{TryConvertFrom1, WithLocal},
173        Encryptor, KeySwitchWithId, Matrix, MatrixEntity, MatrixMut, MultiPartyDecryptor,
174        RowEntity, RowMut,
175    };
176    use itertools::Itertools;
177    use num_traits::{ToPrimitive, Zero};
178
179    use super::*;
180
181    type Mat = Vec<Vec<u64>>;
182
183    pub(super) struct BatchedFheBools<C> {
184        pub(super) data: Vec<C>,
185    }
186
187    impl<C: MatrixMut<MatElement = u64>> BatchedFheBools<C>
188    where
189        C::R: RowEntity + RowMut,
190    {
191        pub(super) fn extract(&self, index: usize) -> C::R {
192            BoolEvaluator::with_local(|e| {
193                let ring_size = e.parameters().rlwe_n().0;
194                let ct_index = index / ring_size;
195                let coeff_index = index % ring_size;
196                let mut lwe_out = C::R::zeros(e.parameters().rlwe_n().0 + 1);
197                sample_extract(
198                    &mut lwe_out,
199                    &self.data[ct_index],
200                    e.pbs_info().modop_rlweq(),
201                    coeff_index,
202                );
203                lwe_out
204            })
205        }
206    }
207
208    pub(super) struct NonInteractiveBatchedFheBools<C> {
209        data: Vec<C>,
210    }
211
212    impl<M: MatrixEntity + MatrixMut<MatElement = u64>> From<&(Vec<M::R>, [u8; 32])>
213        for NonInteractiveBatchedFheBools<M>
214    where
215        <M as Matrix>::R: RowMut,
216    {
217        fn from(value: &(Vec<M::R>, [u8; 32])) -> Self {
218            BoolEvaluator::with_local(|e| {
219                let parameters = e.parameters();
220                let ring_size = parameters.rlwe_n().0;
221                let rlwe_q = parameters.rlwe_q();
222
223                let mut prng = DefaultSecureRng::new_seeded(value.1);
224                let rlwes = value
225                    .0
226                    .iter()
227                    .map(|partb| {
228                        let mut rlwe = M::zeros(2, ring_size);
229
230                        // sample A
231                        RandomFillUniformInModulus::random_fill(
232                            &mut prng,
233                            rlwe_q,
234                            rlwe.get_row_mut(0),
235                        );
236
237                        // Copy over B
238                        rlwe.get_row_mut(1).copy_from_slice(partb.as_ref());
239
240                        rlwe
241                    })
242                    .collect_vec();
243                Self { data: rlwes }
244            })
245        }
246    }
247
248    impl<K> Encryptor<[bool], NonInteractiveBatchedFheBools<Mat>> for K
249    where
250        K: Encryptor<[bool], (Mat, [u8; 32])>,
251    {
252        fn encrypt(&self, m: &[bool]) -> NonInteractiveBatchedFheBools<Mat> {
253            NonInteractiveBatchedFheBools::from(&K::encrypt(&self, m))
254        }
255    }
256
257    impl<K> Encryptor<[bool], (Mat, [u8; 32])> for K
258    where
259        K: NonInteractiveMultiPartyClientKey,
260        <Mat as Matrix>::R:
261            TryConvertFrom1<[K::Element], CiphertextModulus<<Mat as Matrix>::MatElement>>,
262    {
263        fn encrypt(&self, m: &[bool]) -> (Mat, [u8; 32]) {
264            BoolEvaluator::with_local(|e| {
265                DefaultSecureRng::with_local_mut(|rng| {
266                    let parameters = e.parameters();
267                    let ring_size = parameters.rlwe_n().0;
268
269                    let rlwe_count = ((m.len() as f64 / ring_size as f64).ceil())
270                        .to_usize()
271                        .unwrap();
272
273                    let mut seed = <DefaultSecureRng as NewWithSeed>::Seed::default();
274                    rng.fill_bytes(&mut seed);
275                    let mut prng = DefaultSecureRng::new_seeded(seed);
276
277                    let sk_u = self.sk_u_rlwe();
278
279                    // encrypt `m` into ceil(len(m)/N) RLWE ciphertexts
280                    let rlwes = (0..rlwe_count)
281                        .map(|index| {
282                            let mut message = vec![<Mat as Matrix>::MatElement::zero(); ring_size];
283                            m[(index * ring_size)..std::cmp::min(m.len(), (index + 1) * ring_size)]
284                                .iter()
285                                .enumerate()
286                                .for_each(|(i, v)| {
287                                    if *v {
288                                        message[i] = parameters.rlwe_q().true_el()
289                                    } else {
290                                        message[i] = parameters.rlwe_q().false_el()
291                                    }
292                                });
293
294                            // encrypt message
295                            let mut rlwe_out =
296                                <<Mat as Matrix>::R as RowEntity>::zeros(parameters.rlwe_n().0);
297
298                            secret_key_encrypt_rlwe(
299                                &message,
300                                &mut rlwe_out,
301                                &sk_u,
302                                e.pbs_info().modop_rlweq(),
303                                e.pbs_info().nttop_rlweq(),
304                                &mut prng,
305                                rng,
306                            );
307
308                            rlwe_out
309                        })
310                        .collect_vec();
311
312                    (rlwes, seed)
313                })
314            })
315        }
316    }
317
318    impl KeySwitchWithId<Mat> for Mat {
319        fn key_switch(&self, user_id: usize) -> Mat {
320            BoolEvaluator::with_local(|e| {
321                let server_key = BOOL_SERVER_KEY.get().unwrap();
322                let ksk = server_key.ui_to_s_ksk(user_id);
323                let decomposer = e.ni_ui_to_s_ks_decomposer().as_ref().unwrap();
324
325                // perform key switch
326                key_switch(
327                    self,
328                    ksk.as_ref(),
329                    ksk.shoup_repr(),
330                    decomposer,
331                    e.pbs_info().nttop_rlweq(),
332                    e.pbs_info().modop_rlweq(),
333                )
334            })
335        }
336    }
337
338    impl<C> KeySwitchWithId<BatchedFheBools<C>> for NonInteractiveBatchedFheBools<C>
339    where
340        C: KeySwitchWithId<C>,
341    {
342        fn key_switch(&self, user_id: usize) -> BatchedFheBools<C> {
343            let data = self
344                .data
345                .iter()
346                .map(|c| c.key_switch(user_id))
347                .collect_vec();
348            BatchedFheBools { data }
349        }
350    }
351}
352
353#[cfg(test)]
354mod tests {
355    use impl_enc_dec::NonInteractiveBatchedFheBools;
356    use itertools::{izip, Itertools};
357    use num_traits::{FromPrimitive, PrimInt, ToPrimitive, Zero};
358    use rand::{thread_rng, RngCore};
359
360    use crate::{
361        backend::{GetModulus, Modulus},
362        bool::{
363            evaluator::{BoolEncoding, BooleanGates},
364            keys::SinglePartyClientKey,
365        },
366        lwe::decrypt_lwe,
367        rgsw::decrypt_rlwe,
368        utils::{Stats, TryConvertFrom1},
369        ArithmeticOps, Encoder, Encryptor, KeySwitchWithId, ModInit, MultiPartyDecryptor, NttInit,
370        Row, VectorOps,
371    };
372
373    use super::*;
374
375    pub(crate) fn ideal_sk_rlwe(cks: &[ClientKey]) -> Vec<i32> {
376        let mut ideal_rlwe_sk = cks[0].sk_rlwe();
377        cks.iter().skip(1).for_each(|k| {
378            let sk_rlwe = k.sk_rlwe();
379            izip!(ideal_rlwe_sk.iter_mut(), sk_rlwe.iter()).for_each(|(a, b)| {
380                *a = *a + b;
381            });
382        });
383        ideal_rlwe_sk
384    }
385
386    pub(crate) fn measure_noise_lwe<
387        R: Row,
388        S,
389        Modop: ArithmeticOps<Element = R::Element>
390            + GetModulus<M = CiphertextModulus<R::Element>, Element = R::Element>,
391    >(
392        lwe_ct: &R,
393        m_expected: R::Element,
394        sk: &[S],
395        modop: &Modop,
396    ) -> f64
397    where
398        R: TryConvertFrom1<[S], CiphertextModulus<R::Element>>,
399        R::Element: Zero + FromPrimitive + PrimInt,
400    {
401        let noisy_m = decrypt_lwe(lwe_ct, &sk, modop);
402        let noise = modop.sub(&m_expected, &noisy_m);
403        modop
404            .modulus()
405            .map_element_to_i64(&noise)
406            .abs()
407            .to_f64()
408            .unwrap()
409            .log2()
410    }
411
412    #[test]
413    fn non_interactive_mp_bool_nand() {
414        set_parameter_set(ParameterSelector::NonInteractiveMultiPartyLessThanOrEqualTo16);
415        let mut seed = [0u8; 32];
416        thread_rng().fill_bytes(&mut seed);
417        set_common_reference_seed(seed);
418
419        let parties = 2;
420
421        let cks = (0..parties).map(|_| gen_client_key()).collect_vec();
422
423        let key_shares = cks
424            .iter()
425            .enumerate()
426            .map(|(user_index, ck)| gen_server_key_share(user_index, parties, ck))
427            .collect_vec();
428
429        let seeded_server_key = aggregate_server_key_shares(&key_shares);
430        seeded_server_key.set_server_key();
431
432        let parameters = BoolEvaluator::with_local(|e| e.parameters().clone());
433        let rlwe_modop = parameters.default_rlwe_modop();
434
435        let ideal_sk_rlwe = ideal_sk_rlwe(&cks);
436
437        let mut m0 = false;
438        let mut m1 = true;
439
440        let mut ct0 = {
441            let ct: NonInteractiveBatchedFheBools<_> = cks[0].encrypt(vec![m0].as_slice());
442            let ct = ct.key_switch(0);
443            ct.extract(0)
444        };
445        let mut ct1 = {
446            let ct: NonInteractiveBatchedFheBools<_> = cks[1].encrypt(vec![m1].as_slice());
447            let ct = ct.key_switch(1);
448            ct.extract(0)
449        };
450
451        for _ in 0..100 {
452            let ct_out =
453                BoolEvaluator::with_local_mut(|e| e.xor(&ct0, &ct1, RuntimeServerKey::global()));
454
455            let decryption_shares = cks
456                .iter()
457                .map(|k| k.gen_decryption_share(&ct_out))
458                .collect_vec();
459            let m_out = cks[0].aggregate_decryption_shares(&ct_out, &decryption_shares);
460
461            let m_expected = (m0 ^ m1);
462
463            {
464                let noise = measure_noise_lwe(
465                    &ct_out,
466                    parameters.rlwe_q().encode(m_expected),
467                    &ideal_sk_rlwe,
468                    &rlwe_modop,
469                );
470                println!("Noise: {noise}");
471            }
472
473            assert!(m_out == m_expected, "Expected {m_expected} but got {m_out}");
474
475            m1 = m0;
476            m0 = m_out;
477
478            ct1 = ct0;
479            ct0 = ct_out;
480        }
481    }
482
483    #[test]
484    fn trialtest() {
485        set_parameter_set(ParameterSelector::NonInteractiveMultiPartyLessThanOrEqualTo16);
486        set_common_reference_seed([2; 32]);
487
488        let parties = 2;
489
490        let cks = (0..parties).map(|_| gen_client_key()).collect_vec();
491
492        let key_shares = cks
493            .iter()
494            .enumerate()
495            .map(|(user_index, ck)| gen_server_key_share(user_index, parties, ck))
496            .collect_vec();
497
498        let seeded_server_key = aggregate_server_key_shares(&key_shares);
499        seeded_server_key.set_server_key();
500
501        let m = vec![false, true];
502        let ct: NonInteractiveBatchedFheBools<_> = cks[0].encrypt(m.as_slice());
503        let ct = ct.key_switch(0);
504
505        let parameters = BoolEvaluator::with_local(|e| e.parameters().clone());
506        let nttop = NttBackendU64::new(parameters.rlwe_q(), parameters.rlwe_n().0);
507        let rlwe_q_modop = ModularOpsU64::new(*parameters.rlwe_q());
508
509        let mut ideal_rlwe_sk = vec![0i32; parameters.rlwe_n().0];
510        cks.iter().for_each(|k| {
511            let sk_rlwe = k.sk_rlwe();
512            izip!(ideal_rlwe_sk.iter_mut(), sk_rlwe.iter()).for_each(|(a, b)| {
513                *a = *a + b;
514            });
515        });
516
517        let message = m
518            .iter()
519            .map(|b| parameters.rlwe_q().encode(*b))
520            .collect_vec();
521
522        let mut m_out = vec![0u64; parameters.rlwe_n().0];
523        decrypt_rlwe(
524            &ct.data[0],
525            &ideal_rlwe_sk,
526            &mut m_out,
527            &nttop,
528            &rlwe_q_modop,
529        );
530
531        let mut diff = m_out;
532        rlwe_q_modop.elwise_sub_mut(diff.as_mut_slice(), message.as_ref());
533
534        let mut stats = Stats::new();
535        stats.add_more(&Vec::<i64>::try_convert_from(
536            diff.as_slice(),
537            parameters.rlwe_q(),
538        ));
539        println!("Noise: {}", stats.std_dev().abs().log2());
540    }
541}