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