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,
 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 enum ParameterSelector {
 41    NonInteractiveLTE2Party,
 42    NonInteractiveLTE4Party,
 43}
 44
 45pub fn set_parameter_set(select: ParameterSelector) {
 46    match select {
 47        ParameterSelector::NonInteractiveLTE2Party => {
 48            BOOL_EVALUATOR.with_borrow_mut(|v| *v = Some(BoolEvaluator::new(NI_2P)));
 49        }
 50        ParameterSelector::NonInteractiveLTE4Party => {
 51            BOOL_EVALUATOR.with_borrow_mut(|v| *v = Some(BoolEvaluator::new(NI_4P)));
 52        }
 53
 54        _ => {
 55            panic!("Paramerters not supported")
 56        }
 57    }
 58}
 59
 60pub fn set_common_reference_seed(seed: [u8; 32]) {
 61    BoolEvaluator::with_local(|e| {
 62        assert_eq!(
 63            e.parameters().variant(),
 64            &ParameterVariant::NonInteractiveMultiParty,
 65            "Set parameters do not support Non interactive multi-party"
 66        );
 67    });
 68
 69    assert!(
 70        MULTI_PARTY_CRS
 71            .set(NonInteractiveMultiPartyCrs { seed: seed })
 72            .is_ok(),
 73        "Attempted to set MP SEED twice."
 74    )
 75}
 76
 77pub fn gen_client_key() -> ClientKey {
 78    BoolEvaluator::with_local(|e| e.client_key())
 79}
 80
 81pub fn gen_server_key_share(
 82    user_id: usize,
 83    total_users: usize,
 84    client_key: &ClientKey,
 85) -> CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<
 86    Vec<Vec<u64>>,
 87    BoolParameters<u64>,
 88    NonInteractiveMultiPartyCrs<[u8; 32]>,
 89> {
 90    BoolEvaluator::with_local(|e| {
 91        let cr_seed = NonInteractiveMultiPartyCrs::global();
 92        e.gen_non_interactive_multi_party_key_share(cr_seed, user_id, total_users, client_key)
 93    })
 94}
 95
 96pub fn aggregate_server_key_shares(
 97    shares: &[CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<
 98        Vec<Vec<u64>>,
 99        BoolParameters<u64>,
100        NonInteractiveMultiPartyCrs<[u8; 32]>,
101    >],
102) -> SeededNonInteractiveMultiPartyServerKey<
103    Vec<Vec<u64>>,
104    NonInteractiveMultiPartyCrs<[u8; 32]>,
105    BoolParameters<u64>,
106> {
107    BoolEvaluator::with_local(|e| {
108        let cr_seed = NonInteractiveMultiPartyCrs::global();
109        e.aggregate_non_interactive_multi_party_server_key_shares(cr_seed, shares)
110    })
111}
112
113impl
114    SeededNonInteractiveMultiPartyServerKey<
115        Vec<Vec<u64>>,
116        NonInteractiveMultiPartyCrs<[u8; 32]>,
117        BoolParameters<u64>,
118    >
119{
120    pub fn set_server_key(&self) {
121        let eval_key = NonInteractiveServerKeyEvaluationDomain::<
122            _,
123            BoolParameters<u64>,
124            DefaultSecureRng,
125            NttBackendU64,
126        >::from(self);
127        assert!(
128            BOOL_SERVER_KEY
129                .set(ShoupNonInteractiveServerKeyEvaluationDomain::from(eval_key))
130                .is_ok(),
131            "Attempted to set server key twice!"
132        );
133    }
134}
135
136impl Global for NonInteractiveMultiPartyCrs<[u8; 32]> {
137    fn global() -> &'static Self {
138        MULTI_PARTY_CRS
139            .get()
140            .expect("Non-interactive multi-party common reference string not set")
141    }
142}
143
144// BOOL EVALUATOR //
145impl WithLocal for BoolEvaluator {
146    fn with_local<F, R>(func: F) -> R
147    where
148        F: Fn(&Self) -> R,
149    {
150        BOOL_EVALUATOR.with_borrow(|s| func(s.as_ref().expect("Parameters not set")))
151    }
152
153    fn with_local_mut<F, R>(func: F) -> R
154    where
155        F: Fn(&mut Self) -> R,
156    {
157        BOOL_EVALUATOR.with_borrow_mut(|s| func(s.as_mut().expect("Parameters not set")))
158    }
159
160    fn with_local_mut_mut<F, R>(func: &mut F) -> R
161    where
162        F: FnMut(&mut Self) -> R,
163    {
164        BOOL_EVALUATOR.with_borrow_mut(|s| func(s.as_mut().expect("Parameters not set")))
165    }
166}
167
168pub(crate) type RuntimeServerKey = ShoupNonInteractiveServerKeyEvaluationDomain<Vec<Vec<u64>>>;
169impl Global for RuntimeServerKey {
170    fn global() -> &'static Self {
171        BOOL_SERVER_KEY.get().expect("Server key not set!")
172    }
173}
174
175/// Batch of bool ciphertexts stored as vector of RLWE ciphertext under user j's
176/// RLWE secret `u_j`
177///
178/// To use the bool ciphertexts in multi-party protocol first key switch the
179/// ciphertexts from u_j to ideal RLWE secret `s` with
180/// `self.key_switch(user_id)` where `user_id` is user j's id. Key switch
181/// returns `BatchedFheBools` that stored key vector of key switched RLWE
182/// ciphertext.
183pub(super) struct NonInteractiveBatchedFheBools<C> {
184    data: Vec<C>,
185}
186
187/// Batch of Bool cipphertexts stored as vector of RLWE ciphertexts under the
188/// ideal RLWE secret key `s` of the protocol
189///
190/// Bool ciphertext at `index` can be extracted from the coefficient at `index %
191/// N` of `index / N`th RLWE ciphertext.
192///
193/// To extract bool ciphertext at `index` as LWE ciphertext use
194/// `self.extract(index)`
195pub(super) struct BatchedFheBools<C> {
196    pub(in super::super) data: Vec<C>,
197}
198
199/// Non interactive multi-party specfic encryptor decryptor routines
200mod impl_enc_dec {
201    use crate::{
202        bool::{evaluator::BoolEncoding, keys::NonInteractiveMultiPartyClientKey},
203        multi_party::{
204            multi_party_aggregate_decryption_shares_and_decrypt, multi_party_decryption_share,
205        },
206        pbs::{sample_extract, PbsInfo, WithShoupRepr},
207        random::{NewWithSeed, RandomFillUniformInModulus},
208        rgsw::{rlwe_key_switch, seeded_secret_key_encrypt_rlwe},
209        utils::TryConvertFrom1,
210        Encryptor, KeySwitchWithId, Matrix, MatrixEntity, MatrixMut, MultiPartyDecryptor,
211        RowEntity, RowMut,
212    };
213    use itertools::Itertools;
214    use num_traits::{ToPrimitive, Zero};
215
216    use super::*;
217
218    type Mat = Vec<Vec<u64>>;
219
220    // Implement `extract` to extract Bool LWE ciphertext at `index` from
221    // `BatchedFheBools`
222    impl<C: MatrixMut<MatElement = u64>> BatchedFheBools<C>
223    where
224        C::R: RowEntity + RowMut,
225    {
226        pub(super) fn extract(&self, index: usize) -> C::R {
227            BoolEvaluator::with_local(|e| {
228                let ring_size = e.parameters().rlwe_n().0;
229                let ct_index = index / ring_size;
230                let coeff_index = index % ring_size;
231                let mut lwe_out = C::R::zeros(e.parameters().rlwe_n().0 + 1);
232                sample_extract(
233                    &mut lwe_out,
234                    &self.data[ct_index],
235                    e.pbs_info().modop_rlweq(),
236                    coeff_index,
237                );
238                lwe_out
239            })
240        }
241    }
242
243    impl<M: MatrixEntity + MatrixMut<MatElement = u64>> From<&(Vec<M::R>, [u8; 32])>
244        for NonInteractiveBatchedFheBools<M>
245    where
246        <M as Matrix>::R: RowMut,
247    {
248        /// Derive `NonInteractiveBatchedFheBools` from a vector seeded RLWE
249        /// ciphertexts (Vec<RLWE>, Seed)
250        ///
251        /// Unseed the RLWE ciphertexts and store them as vector RLWE
252        /// ciphertexts in `NonInteractiveBatchedFheBools`
253        fn from(value: &(Vec<M::R>, [u8; 32])) -> Self {
254            BoolEvaluator::with_local(|e| {
255                let parameters = e.parameters();
256                let ring_size = parameters.rlwe_n().0;
257                let rlwe_q = parameters.rlwe_q();
258
259                let mut prng = DefaultSecureRng::new_seeded(value.1);
260                let rlwes = value
261                    .0
262                    .iter()
263                    .map(|partb| {
264                        let mut rlwe = M::zeros(2, ring_size);
265
266                        // sample A
267                        RandomFillUniformInModulus::random_fill(
268                            &mut prng,
269                            rlwe_q,
270                            rlwe.get_row_mut(0),
271                        );
272
273                        // Copy over B
274                        rlwe.get_row_mut(1).copy_from_slice(partb.as_ref());
275
276                        rlwe
277                    })
278                    .collect_vec();
279                Self { data: rlwes }
280            })
281        }
282    }
283
284    impl<K> Encryptor<[bool], NonInteractiveBatchedFheBools<Mat>> for K
285    where
286        K: Encryptor<[bool], (Mat, [u8; 32])>,
287    {
288        /// Encrypt a vector bool of arbitrary length as vector of unseeded RLWE
289        /// ciphertexts in `NonInteractiveBatchedFheBools`
290        fn encrypt(&self, m: &[bool]) -> NonInteractiveBatchedFheBools<Mat> {
291            NonInteractiveBatchedFheBools::from(&K::encrypt(&self, m))
292        }
293    }
294
295    impl<K> Encryptor<[bool], (Vec<<Mat as Matrix>::R>, [u8; 32])> for K
296    where
297        K: NonInteractiveMultiPartyClientKey,
298        <Mat as Matrix>::R:
299            TryConvertFrom1<[K::Element], CiphertextModulus<<Mat as Matrix>::MatElement>>,
300    {
301        /// Encrypt a vector of bool of arbitrary length as vector of seeded
302        /// RLWE ciphertexts and returns (Vec<RLWE>, Seed)
303        fn encrypt(&self, m: &[bool]) -> (Mat, [u8; 32]) {
304            BoolEvaluator::with_local(|e| {
305                DefaultSecureRng::with_local_mut(|rng| {
306                    let parameters = e.parameters();
307                    let ring_size = parameters.rlwe_n().0;
308
309                    let rlwe_count = ((m.len() as f64 / ring_size as f64).ceil())
310                        .to_usize()
311                        .unwrap();
312
313                    let mut seed = <DefaultSecureRng as NewWithSeed>::Seed::default();
314                    rng.fill_bytes(&mut seed);
315                    let mut prng = DefaultSecureRng::new_seeded(seed);
316
317                    let sk_u = self.sk_u_rlwe();
318
319                    // encrypt `m` into ceil(len(m)/N) RLWE ciphertexts
320                    let rlwes = (0..rlwe_count)
321                        .map(|index| {
322                            let mut message = vec![<Mat as Matrix>::MatElement::zero(); ring_size];
323                            m[(index * ring_size)..std::cmp::min(m.len(), (index + 1) * ring_size)]
324                                .iter()
325                                .enumerate()
326                                .for_each(|(i, v)| {
327                                    if *v {
328                                        message[i] = parameters.rlwe_q().true_el()
329                                    } else {
330                                        message[i] = parameters.rlwe_q().false_el()
331                                    }
332                                });
333
334                            // encrypt message
335                            let mut rlwe_out =
336                                <<Mat as Matrix>::R as RowEntity>::zeros(parameters.rlwe_n().0);
337
338                            seeded_secret_key_encrypt_rlwe(
339                                &message,
340                                &mut rlwe_out,
341                                &sk_u,
342                                e.pbs_info().modop_rlweq(),
343                                e.pbs_info().nttop_rlweq(),
344                                &mut prng,
345                                rng,
346                            );
347
348                            rlwe_out
349                        })
350                        .collect_vec();
351
352                    (rlwes, seed)
353                })
354            })
355        }
356    }
357
358    impl<K> MultiPartyDecryptor<bool, <Mat as Matrix>::R> for K
359    where
360        K: NonInteractiveMultiPartyClientKey,
361        <Mat as Matrix>::R:
362            TryConvertFrom1<[K::Element], CiphertextModulus<<Mat as Matrix>::MatElement>>,
363    {
364        type DecryptionShare = <Mat as Matrix>::MatElement;
365
366        fn gen_decryption_share(&self, c: &<Mat as Matrix>::R) -> Self::DecryptionShare {
367            BoolEvaluator::with_local(|e| {
368                DefaultSecureRng::with_local_mut(|rng| {
369                    multi_party_decryption_share(
370                        c,
371                        self.sk_rlwe().as_slice(),
372                        e.pbs_info().modop_rlweq(),
373                        rng,
374                    )
375                })
376            })
377        }
378
379        fn aggregate_decryption_shares(
380            &self,
381            c: &<Mat as Matrix>::R,
382            shares: &[Self::DecryptionShare],
383        ) -> bool {
384            BoolEvaluator::with_local(|e| {
385                let noisy_m = multi_party_aggregate_decryption_shares_and_decrypt(
386                    c,
387                    shares,
388                    e.pbs_info().modop_rlweq(),
389                );
390
391                e.pbs_info().rlwe_q().decode(noisy_m)
392            })
393        }
394    }
395
396    impl KeySwitchWithId<Mat> for Mat {
397        /// Key switch RLWE ciphertext `Self` from user j's RLWE secret u_j
398        /// to ideal RLWE secret `s` of non-interactive multi-party protocol.
399        ///
400        /// - user_id: user j's user_id in the protocol
401        fn key_switch(&self, user_id: usize) -> Mat {
402            BoolEvaluator::with_local(|e| {
403                assert!(self.dimension() == (2, e.parameters().rlwe_n().0));
404                let server_key = BOOL_SERVER_KEY.get().unwrap();
405                let ksk = server_key.ui_to_s_ksk(user_id);
406                let decomposer = e.ni_ui_to_s_ks_decomposer().as_ref().unwrap();
407
408                // perform key switch
409                rlwe_key_switch(
410                    self,
411                    ksk.as_ref(),
412                    ksk.shoup_repr(),
413                    decomposer,
414                    e.pbs_info().nttop_rlweq(),
415                    e.pbs_info().modop_rlweq(),
416                )
417            })
418        }
419    }
420
421    impl<C> KeySwitchWithId<BatchedFheBools<C>> for NonInteractiveBatchedFheBools<C>
422    where
423        C: KeySwitchWithId<C>,
424    {
425        /// Key switch `Self`'s vector of RLWE ciphertexts from user j's RLWE
426        /// secret u_j to ideal RLWE secret `s` of non-interactive
427        /// multi-party protocol.
428        ///
429        /// Returns vector of key switched RLWE ciphertext as `BatchedFheBools`
430        /// which can then be used to extract individual Bool LWE ciphertexts.
431        ///
432        /// - user_id: user j's user_id in the protocol
433        fn key_switch(&self, user_id: usize) -> BatchedFheBools<C> {
434            let data = self
435                .data
436                .iter()
437                .map(|c| c.key_switch(user_id))
438                .collect_vec();
439            BatchedFheBools { data }
440        }
441    }
442}
443
444#[cfg(test)]
445mod tests {
446    use itertools::Itertools;
447    use rand::{thread_rng, RngCore};
448
449    use crate::{
450        backend::Modulus,
451        bool::{
452            keys::tests::{ideal_sk_rlwe, measure_noise_lwe},
453            BooleanGates,
454        },
455        lwe::decrypt_lwe,
456        utils::tests::Stats,
457        ArithmeticOps, Encoder, Encryptor, KeySwitchWithId, MultiPartyDecryptor,
458    };
459
460    use super::*;
461
462    #[test]
463    fn non_interactive_mp_bool_nand() {
464        set_parameter_set(ParameterSelector::NonInteractiveLTE2Party);
465        let mut seed = [0u8; 32];
466        thread_rng().fill_bytes(&mut seed);
467        set_common_reference_seed(seed);
468
469        let parties = 2;
470
471        let cks = (0..parties).map(|_| gen_client_key()).collect_vec();
472
473        let key_shares = cks
474            .iter()
475            .enumerate()
476            .map(|(user_index, ck)| gen_server_key_share(user_index, parties, ck))
477            .collect_vec();
478
479        let seeded_server_key = aggregate_server_key_shares(&key_shares);
480        seeded_server_key.set_server_key();
481
482        let parameters = BoolEvaluator::with_local(|e| e.parameters().clone());
483        let rlwe_modop = parameters.default_rlwe_modop();
484
485        let ideal_sk_rlwe = ideal_sk_rlwe(&cks);
486
487        let mut m0 = false;
488        let mut m1 = true;
489
490        let mut ct0 = {
491            let ct: NonInteractiveBatchedFheBools<_> = cks[0].encrypt(vec![m0].as_slice());
492            let ct = ct.key_switch(0);
493            ct.extract(0)
494        };
495        let mut ct1 = {
496            let ct: NonInteractiveBatchedFheBools<_> = cks[1].encrypt(vec![m1].as_slice());
497            let ct = ct.key_switch(1);
498            ct.extract(0)
499        };
500
501        let mut stats = Stats::new();
502
503        for _ in 0..1000 {
504            // let now = std::time::Instant::now();
505            let ct_out =
506                BoolEvaluator::with_local_mut(|e| e.xor(&ct0, &ct1, RuntimeServerKey::global()));
507            // println!("Time: {:?}", now.elapsed());
508
509            let decryption_shares = cks
510                .iter()
511                .map(|k| k.gen_decryption_share(&ct_out))
512                .collect_vec();
513            let m_out = cks[0].aggregate_decryption_shares(&ct_out, &decryption_shares);
514
515            let m_expected = m0 ^ m1;
516
517            {
518                // let noise = measure_noise_lwe(
519                //     &ct_out,
520                //     parameters.rlwe_q().encode(m_expected),
521                //     &ideal_sk_rlwe,
522                //     &rlwe_modop,
523                // );
524                // println!("Noise: {noise}");
525
526                let noisy_m = decrypt_lwe(&ct_out, &ideal_sk_rlwe, &rlwe_modop);
527                let noise = rlwe_modop.sub(&parameters.rlwe_q().encode(m_expected), &noisy_m);
528                stats.add_more(&vec![parameters.rlwe_q().map_element_to_i64(&noise)]);
529            }
530
531            assert!(m_out == m_expected, "Expected {m_expected} but got {m_out}");
532
533            m1 = m0;
534            m0 = m_out;
535
536            ct1 = ct0;
537            ct0 = ct_out;
538        }
539
540        println!("Noise std dev log2: {}", stats.std_dev().abs().log2());
541    }
542}