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  
372
373    #[test]
374    fn non_interactive_mp_bool_nand() {
375        set_parameter_set(ParameterSelector::NonInteractiveMultiPartyLessThanOrEqualTo16);
376        let mut seed = [0u8; 32];
377        thread_rng().fill_bytes(&mut seed);
378        set_common_reference_seed(seed);
379
380        let parties = 2;
381
382        let cks = (0..parties).map(|_| gen_client_key()).collect_vec();
383
384        let key_shares = cks
385            .iter()
386            .enumerate()
387            .map(|(user_index, ck)| gen_server_key_share(user_index, parties, ck))
388            .collect_vec();
389
390        let seeded_server_key = aggregate_server_key_shares(&key_shares);
391        seeded_server_key.set_server_key();
392
393        let parameters = BoolEvaluator::with_local(|e| e.parameters().clone());
394        let rlwe_modop = parameters.default_rlwe_modop();
395
396        let ideal_sk_rlwe = ideal_sk_rlwe(&cks);
397
398        let mut m0 = false;
399        let mut m1 = true;
400
401        let mut ct0 = {
402            let ct: NonInteractiveBatchedFheBools<_> = cks[0].encrypt(vec![m0].as_slice());
403            let ct = ct.key_switch(0);
404            ct.extract(0)
405        };
406        let mut ct1 = {
407            let ct: NonInteractiveBatchedFheBools<_> = cks[1].encrypt(vec![m1].as_slice());
408            let ct = ct.key_switch(1);
409            ct.extract(0)
410        };
411
412        for _ in 0..1000 {
413            // let now = std::time::Instant::now();
414            let ct_out =
415                BoolEvaluator::with_local_mut(|e| e.xor(&ct0, &ct1, RuntimeServerKey::global()));
416            // println!("Time: {:?}", now.elapsed());
417
418            let decryption_shares = cks
419                .iter()
420                .map(|k| k.gen_decryption_share(&ct_out))
421                .collect_vec();
422            let m_out = cks[0].aggregate_decryption_shares(&ct_out, &decryption_shares);
423
424            let m_expected = m0 ^ m1;
425
426            {
427                let noise = measure_noise_lwe(
428                    &ct_out,
429                    parameters.rlwe_q().encode(m_expected),
430                    &ideal_sk_rlwe,
431                    &rlwe_modop,
432                );
433                println!("Noise: {noise}");
434            }
435
436            assert!(m_out == m_expected, "Expected {m_expected} but got {m_out}");
437
438            m1 = m0;
439            m0 = m_out;
440
441            ct1 = ct0;
442            ct0 = ct_out;
443        }
444    }
445
446    #[test]
447    fn trialtest() {
448        set_parameter_set(ParameterSelector::NonInteractiveMultiPartyLessThanOrEqualTo16);
449        set_common_reference_seed([2; 32]);
450
451        let parties = 2;
452
453        let cks = (0..parties).map(|_| gen_client_key()).collect_vec();
454
455        let key_shares = cks
456            .iter()
457            .enumerate()
458            .map(|(user_index, ck)| gen_server_key_share(user_index, parties, ck))
459            .collect_vec();
460
461        let seeded_server_key = aggregate_server_key_shares(&key_shares);
462        seeded_server_key.set_server_key();
463
464        let m = vec![false, true];
465        let ct: NonInteractiveBatchedFheBools<_> = cks[0].encrypt(m.as_slice());
466        let ct = ct.key_switch(0);
467
468        let parameters = BoolEvaluator::with_local(|e| e.parameters().clone());
469        let nttop = NttBackendU64::new(parameters.rlwe_q(), parameters.rlwe_n().0);
470        let rlwe_q_modop = ModularOpsU64::new(*parameters.rlwe_q());
471
472        let mut ideal_rlwe_sk = vec![0i32; parameters.rlwe_n().0];
473        cks.iter().for_each(|k| {
474            let sk_rlwe = k.sk_rlwe();
475            izip!(ideal_rlwe_sk.iter_mut(), sk_rlwe.iter()).for_each(|(a, b)| {
476                *a = *a + b;
477            });
478        });
479
480        let message = m
481            .iter()
482            .map(|b| parameters.rlwe_q().encode(*b))
483            .collect_vec();
484
485        let mut m_out = vec![0u64; parameters.rlwe_n().0];
486        decrypt_rlwe(
487            &ct.data[0],
488            &ideal_rlwe_sk,
489            &mut m_out,
490            &nttop,
491            &rlwe_q_modop,
492        );
493
494        let mut diff = m_out;
495        rlwe_q_modop.elwise_sub_mut(diff.as_mut_slice(), message.as_ref());
496
497        let mut stats = Stats::new();
498        stats.add_more(&Vec::<i64>::try_convert_from(
499            diff.as_slice(),
500            parameters.rlwe_q(),
501        ));
502        println!("Noise: {}", stats.std_dev().abs().log2());
503    }
504}