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 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    impl<E> MultiPartyDecryptor<bool, <Mat as Matrix>::R>
353        for super::super::keys::ClientKey<[u8; 32], E>
354    {
355        type DecryptionShare = <Mat as Matrix>::MatElement;
356
357        fn gen_decryption_share(&self, c: &<Mat as Matrix>::R) -> Self::DecryptionShare {
358            BoolEvaluator::with_local(|e| e.multi_party_decryption_share(c, self))
359        }
360
361        fn aggregate_decryption_shares(
362            &self,
363            c: &<Mat as Matrix>::R,
364            shares: &[Self::DecryptionShare],
365        ) -> bool {
366            BoolEvaluator::with_local(|e| e.multi_party_decrypt(shares, c))
367        }
368    }
369}
370
371#[cfg(test)]
372mod tests {
373    use impl_enc_dec::NonInteractiveBatchedFheBools;
374    use itertools::{izip, Itertools};
375    use num_traits::{FromPrimitive, PrimInt, ToPrimitive, Zero};
376    use rand::{thread_rng, RngCore};
377
378    use crate::{
379        backend::{GetModulus, Modulus},
380        bool::{
381            evaluator::{BoolEncoding, BooleanGates},
382            keys::SinglePartyClientKey,
383        },
384        lwe::decrypt_lwe,
385        rgsw::decrypt_rlwe,
386        utils::{Stats, TryConvertFrom1},
387        ArithmeticOps, Encoder, Encryptor, KeySwitchWithId, ModInit, MultiPartyDecryptor, NttInit,
388        Row, VectorOps,
389    };
390
391    use super::*;
392
393    pub(crate) fn ideal_sk_rlwe(cks: &[ClientKey]) -> Vec<i32> {
394        let mut ideal_rlwe_sk = cks[0].sk_rlwe();
395        cks.iter().skip(1).for_each(|k| {
396            let sk_rlwe = k.sk_rlwe();
397            izip!(ideal_rlwe_sk.iter_mut(), sk_rlwe.iter()).for_each(|(a, b)| {
398                *a = *a + b;
399            });
400        });
401        ideal_rlwe_sk
402    }
403
404    pub(crate) fn measure_noise_lwe<
405        R: Row,
406        S,
407        Modop: ArithmeticOps<Element = R::Element>
408            + GetModulus<M = CiphertextModulus<R::Element>, Element = R::Element>,
409    >(
410        lwe_ct: &R,
411        m_expected: R::Element,
412        sk: &[S],
413        modop: &Modop,
414    ) -> f64
415    where
416        R: TryConvertFrom1<[S], CiphertextModulus<R::Element>>,
417        R::Element: Zero + FromPrimitive + PrimInt,
418    {
419        let noisy_m = decrypt_lwe(lwe_ct, &sk, modop);
420        let noise = modop.sub(&m_expected, &noisy_m);
421        modop
422            .modulus()
423            .map_element_to_i64(&noise)
424            .abs()
425            .to_f64()
426            .unwrap()
427            .log2()
428    }
429
430    #[test]
431    fn non_interactive_mp_bool_nand() {
432        set_parameter_set(ParameterSelector::NonInteractiveMultiPartyLessThanOrEqualTo16);
433        let mut seed = [0u8; 32];
434        thread_rng().fill_bytes(&mut seed);
435        set_common_reference_seed(seed);
436
437        let parties = 2;
438
439        let cks = (0..parties).map(|_| gen_client_key()).collect_vec();
440
441        let key_shares = cks
442            .iter()
443            .enumerate()
444            .map(|(user_index, ck)| gen_server_key_share(user_index, parties, ck))
445            .collect_vec();
446
447        let seeded_server_key = aggregate_server_key_shares(&key_shares);
448        seeded_server_key.set_server_key();
449
450        let parameters = BoolEvaluator::with_local(|e| e.parameters().clone());
451        let rlwe_modop = parameters.default_rlwe_modop();
452
453        let ideal_sk_rlwe = ideal_sk_rlwe(&cks);
454
455        let mut m0 = false;
456        let mut m1 = true;
457
458        let mut ct0 = {
459            let ct: NonInteractiveBatchedFheBools<_> = cks[0].encrypt(vec![m0].as_slice());
460            let ct = ct.key_switch(0);
461            ct.extract(0)
462        };
463        let mut ct1 = {
464            let ct: NonInteractiveBatchedFheBools<_> = cks[1].encrypt(vec![m1].as_slice());
465            let ct = ct.key_switch(1);
466            ct.extract(0)
467        };
468
469        for _ in 0..100 {
470            let ct_out =
471                BoolEvaluator::with_local_mut(|e| e.xor(&ct0, &ct1, RuntimeServerKey::global()));
472
473            let decryption_shares = cks
474                .iter()
475                .map(|k| k.gen_decryption_share(&ct_out))
476                .collect_vec();
477            let m_out = cks[0].aggregate_decryption_shares(&ct_out, &decryption_shares);
478
479            let m_expected = (m0 ^ m1);
480
481            {
482                let noise = measure_noise_lwe(
483                    &ct_out,
484                    parameters.rlwe_q().encode(m_expected),
485                    &ideal_sk_rlwe,
486                    &rlwe_modop,
487                );
488                println!("Noise: {noise}");
489            }
490
491            assert!(m_out == m_expected, "Expected {m_expected} but got {m_out}");
492
493            m1 = m0;
494            m0 = m_out;
495
496            ct1 = ct0;
497            ct0 = ct_out;
498        }
499    }
500
501    #[test]
502    fn trialtest() {
503        set_parameter_set(ParameterSelector::NonInteractiveMultiPartyLessThanOrEqualTo16);
504        set_common_reference_seed([2; 32]);
505
506        let parties = 2;
507
508        let cks = (0..parties).map(|_| gen_client_key()).collect_vec();
509
510        let key_shares = cks
511            .iter()
512            .enumerate()
513            .map(|(user_index, ck)| gen_server_key_share(user_index, parties, ck))
514            .collect_vec();
515
516        let seeded_server_key = aggregate_server_key_shares(&key_shares);
517        seeded_server_key.set_server_key();
518
519        let m = vec![false, true];
520        let ct: NonInteractiveBatchedFheBools<_> = cks[0].encrypt(m.as_slice());
521        let ct = ct.key_switch(0);
522
523        let parameters = BoolEvaluator::with_local(|e| e.parameters().clone());
524        let nttop = NttBackendU64::new(parameters.rlwe_q(), parameters.rlwe_n().0);
525        let rlwe_q_modop = ModularOpsU64::new(*parameters.rlwe_q());
526
527        let mut ideal_rlwe_sk = vec![0i32; parameters.rlwe_n().0];
528        cks.iter().for_each(|k| {
529            let sk_rlwe = k.sk_rlwe();
530            izip!(ideal_rlwe_sk.iter_mut(), sk_rlwe.iter()).for_each(|(a, b)| {
531                *a = *a + b;
532            });
533        });
534
535        let message = m
536            .iter()
537            .map(|b| parameters.rlwe_q().encode(*b))
538            .collect_vec();
539
540        let mut m_out = vec![0u64; parameters.rlwe_n().0];
541        decrypt_rlwe(
542            &ct.data[0],
543            &ideal_rlwe_sk,
544            &mut m_out,
545            &nttop,
546            &rlwe_q_modop,
547        );
548
549        let mut diff = m_out;
550        rlwe_q_modop.elwise_sub_mut(diff.as_mut_slice(), message.as_ref());
551
552        let mut stats = Stats::new();
553        stats.add_more(&Vec::<i64>::try_convert_from(
554            diff.as_slice(),
555            parameters.rlwe_q(),
556        ));
557        println!("Noise: {}", stats.std_dev().abs().log2());
558    }
559}