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