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