arnaucube
arnaucube/phantom-zonePublic Self-hosted repository
phantom-zone/src/bool/ni_mp_api.rs
src/bool/ni_mp_api.rsRaw
  1use std::{borrow::Borrow, 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, NI_2P, NI_4P_HB_FR, NI_8P},
 19    ClientKey,
 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    static ACTIVE_PARAMETER_SET: RefCell<Option<ParameterSelector>> = RefCell::new(None);
 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
 39#[derive(Copy, Clone)]
 40pub enum ParameterSelector {
 41    NonInteractiveLTE2Party,
 42    NonInteractiveLTE4Party,
 43    NonInteractiveLTE8Party,
 44}
 45
 46pub fn set_parameter_set(select: ParameterSelector) {
 47    match select {
 48        ParameterSelector::NonInteractiveLTE2Party => {
 49            BOOL_EVALUATOR.with_borrow_mut(|v| *v = Some(BoolEvaluator::new(NI_2P)));
 50        }
 51        ParameterSelector::NonInteractiveLTE4Party => {
 52            BOOL_EVALUATOR.with_borrow_mut(|v| *v = Some(BoolEvaluator::new(NI_4P_HB_FR)));
 53        }
 54        ParameterSelector::NonInteractiveLTE8Party => {
 55            BOOL_EVALUATOR.with_borrow_mut(|v| *v = Some(BoolEvaluator::new(NI_8P)));
 56        }
 57    }
 58    ACTIVE_PARAMETER_SET.with_borrow_mut(|v| *v = Some(select));
 59}
 60
 61pub fn get_active_parameter_set() -> ParameterSelector {
 62    ACTIVE_PARAMETER_SET
 63        .borrow()
 64        .take()
 65        .expect("Parameters not set")
 66}
 67
 68pub fn set_common_reference_seed(seed: [u8; 32]) {
 69    BoolEvaluator::with_local(|e| {
 70        assert_eq!(
 71            e.parameters().variant(),
 72            &ParameterVariant::NonInteractiveMultiParty,
 73            "Set parameters do not support Non interactive multi-party"
 74        );
 75    });
 76
 77    assert!(
 78        MULTI_PARTY_CRS
 79            .set(NonInteractiveMultiPartyCrs { seed: seed })
 80            .is_ok(),
 81        "Attempted to set MP SEED twice."
 82    )
 83}
 84
 85pub fn gen_client_key() -> ClientKey {
 86    BoolEvaluator::with_local(|e| e.client_key())
 87}
 88
 89pub fn gen_server_key_share(
 90    user_id: usize,
 91    total_users: usize,
 92    client_key: &ClientKey,
 93) -> CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<
 94    Vec<Vec<u64>>,
 95    BoolParameters<u64>,
 96    NonInteractiveMultiPartyCrs<[u8; 32]>,
 97> {
 98    BoolEvaluator::with_local(|e| {
 99        let cr_seed = NonInteractiveMultiPartyCrs::global();
100        e.gen_non_interactive_multi_party_key_share(cr_seed, user_id, total_users, client_key)
101    })
102}
103
104pub fn aggregate_server_key_shares(
105    shares: &[CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<
106        Vec<Vec<u64>>,
107        BoolParameters<u64>,
108        NonInteractiveMultiPartyCrs<[u8; 32]>,
109    >],
110) -> SeededNonInteractiveMultiPartyServerKey<
111    Vec<Vec<u64>>,
112    NonInteractiveMultiPartyCrs<[u8; 32]>,
113    BoolParameters<u64>,
114> {
115    BoolEvaluator::with_local(|e| {
116        let cr_seed = NonInteractiveMultiPartyCrs::global();
117        e.aggregate_non_interactive_multi_party_server_key_shares(cr_seed, shares)
118    })
119}
120
121impl
122    SeededNonInteractiveMultiPartyServerKey<
123        Vec<Vec<u64>>,
124        NonInteractiveMultiPartyCrs<[u8; 32]>,
125        BoolParameters<u64>,
126    >
127{
128    pub fn set_server_key(&self) {
129        let eval_key = NonInteractiveServerKeyEvaluationDomain::<
130            _,
131            BoolParameters<u64>,
132            DefaultSecureRng,
133            NttBackendU64,
134        >::from(self);
135        assert!(
136            BOOL_SERVER_KEY
137                .set(ShoupNonInteractiveServerKeyEvaluationDomain::from(eval_key))
138                .is_ok(),
139            "Attempted to set server key twice!"
140        );
141    }
142}
143
144impl Global for NonInteractiveMultiPartyCrs<[u8; 32]> {
145    fn global() -> &'static Self {
146        MULTI_PARTY_CRS
147            .get()
148            .expect("Non-interactive multi-party common reference string not set")
149    }
150}
151
152// BOOL EVALUATOR //
153impl WithLocal for BoolEvaluator {
154    fn with_local<F, R>(func: F) -> R
155    where
156        F: Fn(&Self) -> R,
157    {
158        BOOL_EVALUATOR.with_borrow(|s| func(s.as_ref().expect("Parameters not set")))
159    }
160
161    fn with_local_mut<F, R>(func: F) -> R
162    where
163        F: Fn(&mut Self) -> R,
164    {
165        BOOL_EVALUATOR.with_borrow_mut(|s| func(s.as_mut().expect("Parameters not set")))
166    }
167
168    fn with_local_mut_mut<F, R>(func: &mut F) -> R
169    where
170        F: FnMut(&mut Self) -> R,
171    {
172        BOOL_EVALUATOR.with_borrow_mut(|s| func(s.as_mut().expect("Parameters not set")))
173    }
174}
175
176pub(crate) type RuntimeServerKey = ShoupNonInteractiveServerKeyEvaluationDomain<Vec<Vec<u64>>>;
177impl Global for RuntimeServerKey {
178    fn global() -> &'static Self {
179        BOOL_SERVER_KEY.get().expect("Server key not set!")
180    }
181}
182
183/// `Self::data` stores collection of seeded RLWE ciphertexts encrypted unser user j's RLWE secret `u_j`.
184pub struct NonInteractiveSeededFheBools<C, S> {
185    data: Vec<C>,
186    seed: S,
187    count: usize,
188}
189
190/// Batch of bool ciphertexts stored as vector of RLWE ciphertext under user j's
191/// RLWE secret `u_j`
192///
193/// To use the bool ciphertexts in multi-party protocol first key switch the
194/// ciphertexts from u_j to ideal RLWE secret `s` with
195/// `self.key_switch(user_id)` where `user_id` is user j's id. Key switch
196/// returns `BatchedFheBools` which stores vector of key switched RLWE
197/// ciphertext.
198pub struct NonInteractiveBatchedFheBools<C> {
199    data: Vec<C>,
200    count: usize,
201}
202
203/// Non interactive multi-party specfic encryptor decryptor routines
204mod impl_enc_dec {
205    use crate::{
206        bool::{
207            common_mp_enc_dec::BatchedFheBools, evaluator::BoolEncoding,
208            keys::NonInteractiveMultiPartyClientKey,
209        },
210        multi_party::{
211            multi_party_aggregate_decryption_shares_and_decrypt, multi_party_decryption_share,
212        },
213        pbs::{PbsInfo, WithShoupRepr},
214        random::{NewWithSeed, RandomFillUniformInModulus},
215        rgsw::{rlwe_key_switch, seeded_secret_key_encrypt_rlwe},
216        utils::TryConvertFrom1,
217        Encryptor, KeySwitchWithId, Matrix, MatrixEntity, MatrixMut, MultiPartyDecryptor,
218        RowEntity, RowMut,
219    };
220    use itertools::Itertools;
221    use num_traits::{ToPrimitive, Zero};
222
223    use super::*;
224
225    type Mat = Vec<Vec<u64>>;
226
227    impl<C, S> NonInteractiveSeededFheBools<C, S> {
228        /// Unseed `Self`'s collection of RLWE ciphertexts into `NonInteractiveBatchedFheBools`
229        pub fn unseed<M>(&self) -> NonInteractiveBatchedFheBools<M>
230        where
231            NonInteractiveBatchedFheBools<M>: for<'a> From<&'a Self>,
232        {
233            NonInteractiveBatchedFheBools::from(self)
234        }
235    }
236
237    impl<C, S> From<NonInteractiveSeededFheBools<C, S>> for (Vec<C>, S) {
238        fn from(value: NonInteractiveSeededFheBools<C, S>) -> Self {
239            (value.data, value.seed)
240        }
241    }
242
243    impl<K> Encryptor<[bool], NonInteractiveSeededFheBools<<Mat as Matrix>::R, [u8; 32]>> for K
244    where
245        K: NonInteractiveMultiPartyClientKey,
246        <Mat as Matrix>::R:
247            TryConvertFrom1<[K::Element], CiphertextModulus<<Mat as Matrix>::MatElement>>,
248    {
249        /// Encrypt a vector of bool of arbitrary length as vector of seeded
250        /// RLWE ciphertexts and returns (Vec<RLWE>, Seed)
251        fn encrypt(
252            &self,
253            m: &[bool],
254        ) -> NonInteractiveSeededFheBools<<Mat as Matrix>::R, [u8; 32]> {
255            BoolEvaluator::with_local(|e| {
256                DefaultSecureRng::with_local_mut(|rng| {
257                    let parameters = e.parameters();
258                    let ring_size = parameters.rlwe_n().0;
259
260                    let rlwe_count = ((m.len() as f64 / ring_size as f64).ceil())
261                        .to_usize()
262                        .unwrap();
263
264                    let mut seed = <DefaultSecureRng as NewWithSeed>::Seed::default();
265                    rng.fill_bytes(&mut seed);
266                    let mut prng = DefaultSecureRng::new_seeded(seed);
267
268                    let sk_u = self.sk_u_rlwe();
269
270                    // encrypt `m` into ceil(len(m)/N) RLWE ciphertexts
271                    let rlwes = (0..rlwe_count)
272                        .map(|index| {
273                            let mut message = vec![<Mat as Matrix>::MatElement::zero(); ring_size];
274                            m[(index * ring_size)..std::cmp::min(m.len(), (index + 1) * ring_size)]
275                                .iter()
276                                .enumerate()
277                                .for_each(|(i, v)| {
278                                    if *v {
279                                        message[i] = parameters.rlwe_q().true_el()
280                                    } else {
281                                        message[i] = parameters.rlwe_q().false_el()
282                                    }
283                                });
284
285                            // encrypt message
286                            let mut rlwe_out =
287                                <<Mat as Matrix>::R as RowEntity>::zeros(parameters.rlwe_n().0);
288
289                            seeded_secret_key_encrypt_rlwe(
290                                &message,
291                                &mut rlwe_out,
292                                &sk_u,
293                                e.pbs_info().modop_rlweq(),
294                                e.pbs_info().nttop_rlweq(),
295                                &mut prng,
296                                rng,
297                            );
298
299                            rlwe_out
300                        })
301                        .collect_vec();
302
303                    NonInteractiveSeededFheBools {
304                        data: rlwes,
305                        seed,
306                        count: m.len(),
307                    }
308                })
309            })
310        }
311    }
312
313    impl<M: MatrixEntity + MatrixMut<MatElement = u64>>
314        From<&NonInteractiveSeededFheBools<<Mat as Matrix>::R, [u8; 32]>>
315        for NonInteractiveBatchedFheBools<M>
316    where
317        <M as Matrix>::R: RowMut,
318    {
319        /// Derive `NonInteractiveBatchedFheBools` from a vector seeded RLWE
320        /// ciphertexts (Vec<RLWE>, Seed)
321        ///
322        /// Unseed the RLWE ciphertexts and store them as vector RLWE
323        /// ciphertexts in `NonInteractiveBatchedFheBools`
324        fn from(value: &NonInteractiveSeededFheBools<<Mat as Matrix>::R, [u8; 32]>) -> Self {
325            BoolEvaluator::with_local(|e| {
326                let parameters = e.parameters();
327                let ring_size = parameters.rlwe_n().0;
328                let rlwe_q = parameters.rlwe_q();
329
330                let mut prng = DefaultSecureRng::new_seeded(value.seed);
331                let rlwes = value
332                    .data
333                    .iter()
334                    .map(|partb| {
335                        let mut rlwe = M::zeros(2, ring_size);
336
337                        // sample A
338                        RandomFillUniformInModulus::random_fill(
339                            &mut prng,
340                            rlwe_q,
341                            rlwe.get_row_mut(0),
342                        );
343
344                        // Copy over B
345                        rlwe.get_row_mut(1).copy_from_slice(partb.as_ref());
346
347                        rlwe
348                    })
349                    .collect_vec();
350                Self {
351                    data: rlwes,
352                    count: value.count,
353                }
354            })
355        }
356    }
357
358    impl KeySwitchWithId<Mat> for Mat {
359        /// Key switch RLWE ciphertext `Self` from user j's RLWE secret u_j
360        /// to ideal RLWE secret `s` of non-interactive multi-party protocol.
361        ///
362        /// - user_id: user j's user_id in the protocol
363        fn key_switch(&self, user_id: usize) -> Mat {
364            BoolEvaluator::with_local(|e| {
365                assert!(self.dimension() == (2, e.parameters().rlwe_n().0));
366                let server_key = BOOL_SERVER_KEY.get().unwrap();
367                let ksk = server_key.ui_to_s_ksk(user_id);
368                let decomposer = e.ni_ui_to_s_ks_decomposer().as_ref().unwrap();
369
370                // perform key switch
371                rlwe_key_switch(
372                    self,
373                    ksk.as_ref(),
374                    ksk.shoup_repr(),
375                    decomposer,
376                    e.pbs_info().nttop_rlweq(),
377                    e.pbs_info().modop_rlweq(),
378                )
379            })
380        }
381    }
382
383    impl<C> KeySwitchWithId<BatchedFheBools<C>> for NonInteractiveBatchedFheBools<C>
384    where
385        C: KeySwitchWithId<C>,
386    {
387        /// Key switch `Self`'s vector of RLWE ciphertexts from user j's RLWE
388        /// secret u_j to ideal RLWE secret `s` of non-interactive
389        /// multi-party protocol.
390        ///
391        /// Returns vector of key switched RLWE ciphertext as `BatchedFheBools`
392        /// which can then be used to extract individual Bool LWE ciphertexts.
393        ///
394        /// - user_id: user j's user_id in the protocol
395        fn key_switch(&self, user_id: usize) -> BatchedFheBools<C> {
396            let data = self
397                .data
398                .iter()
399                .map(|c| c.key_switch(user_id))
400                .collect_vec();
401            BatchedFheBools::new(data, self.count)
402        }
403    }
404
405    impl<K> MultiPartyDecryptor<bool, <Mat as Matrix>::R> for K
406    where
407        K: NonInteractiveMultiPartyClientKey,
408        <Mat as Matrix>::R:
409            TryConvertFrom1<[K::Element], CiphertextModulus<<Mat as Matrix>::MatElement>>,
410    {
411        type DecryptionShare = <Mat as Matrix>::MatElement;
412
413        fn gen_decryption_share(&self, c: &<Mat as Matrix>::R) -> Self::DecryptionShare {
414            BoolEvaluator::with_local(|e| {
415                DefaultSecureRng::with_local_mut(|rng| {
416                    multi_party_decryption_share(
417                        c,
418                        self.sk_rlwe().as_slice(),
419                        e.pbs_info().modop_rlweq(),
420                        rng,
421                    )
422                })
423            })
424        }
425
426        fn aggregate_decryption_shares(
427            &self,
428            c: &<Mat as Matrix>::R,
429            shares: &[Self::DecryptionShare],
430        ) -> bool {
431            BoolEvaluator::with_local(|e| {
432                let noisy_m = multi_party_aggregate_decryption_shares_and_decrypt(
433                    c,
434                    shares,
435                    e.pbs_info().modop_rlweq(),
436                );
437
438                e.pbs_info().rlwe_q().decode(noisy_m)
439            })
440        }
441    }
442}