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