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/// Batch of bool ciphertexts stored as vector of RLWE ciphertext under user j's
183/// RLWE secret `u_j`
184///
185/// To use the bool ciphertexts in multi-party protocol first key switch the
186/// ciphertexts from u_j to ideal RLWE secret `s` with
187/// `self.key_switch(user_id)` where `user_id` is user j's id. Key switch
188/// returns `BatchedFheBools` that stored key vector of key switched RLWE
189/// ciphertext.
190pub struct NonInteractiveBatchedFheBools<C> {
191    data: Vec<C>,
192}
193
194/// Batch of Bool cipphertexts stored as vector of RLWE ciphertexts under the
195/// ideal RLWE secret key `s` of the protocol
196///
197/// Bool ciphertext at `index` can be extracted from the coefficient at `index %
198/// N` of `index / N`th RLWE ciphertext.
199///
200/// To extract bool ciphertext at `index` as LWE ciphertext use
201/// `self.extract(index)`
202pub struct BatchedFheBools<C> {
203    pub(in super::super) data: Vec<C>,
204}
205
206/// Non interactive multi-party specfic encryptor decryptor routines
207mod impl_enc_dec {
208    use crate::{
209        bool::{evaluator::BoolEncoding, keys::NonInteractiveMultiPartyClientKey},
210        multi_party::{
211            multi_party_aggregate_decryption_shares_and_decrypt, multi_party_decryption_share,
212        },
213        pbs::{sample_extract, 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    // Implement `extract` to extract Bool LWE ciphertext at `index` from
228    // `BatchedFheBools`
229    impl<C: MatrixMut<MatElement = u64>> BatchedFheBools<C>
230    where
231        C::R: RowEntity + RowMut,
232    {
233        pub fn extract(&self, index: usize) -> C::R {
234            BoolEvaluator::with_local(|e| {
235                let ring_size = e.parameters().rlwe_n().0;
236                let ct_index = index / ring_size;
237                let coeff_index = index % ring_size;
238                let mut lwe_out = C::R::zeros(e.parameters().rlwe_n().0 + 1);
239                sample_extract(
240                    &mut lwe_out,
241                    &self.data[ct_index],
242                    e.pbs_info().modop_rlweq(),
243                    coeff_index,
244                );
245                lwe_out
246            })
247        }
248    }
249
250    impl<M: MatrixEntity + MatrixMut<MatElement = u64>> From<&(Vec<M::R>, [u8; 32])>
251        for NonInteractiveBatchedFheBools<M>
252    where
253        <M as Matrix>::R: RowMut,
254    {
255        /// Derive `NonInteractiveBatchedFheBools` from a vector seeded RLWE
256        /// ciphertexts (Vec<RLWE>, Seed)
257        ///
258        /// Unseed the RLWE ciphertexts and store them as vector RLWE
259        /// ciphertexts in `NonInteractiveBatchedFheBools`
260        fn from(value: &(Vec<M::R>, [u8; 32])) -> Self {
261            BoolEvaluator::with_local(|e| {
262                let parameters = e.parameters();
263                let ring_size = parameters.rlwe_n().0;
264                let rlwe_q = parameters.rlwe_q();
265
266                let mut prng = DefaultSecureRng::new_seeded(value.1);
267                let rlwes = value
268                    .0
269                    .iter()
270                    .map(|partb| {
271                        let mut rlwe = M::zeros(2, ring_size);
272
273                        // sample A
274                        RandomFillUniformInModulus::random_fill(
275                            &mut prng,
276                            rlwe_q,
277                            rlwe.get_row_mut(0),
278                        );
279
280                        // Copy over B
281                        rlwe.get_row_mut(1).copy_from_slice(partb.as_ref());
282
283                        rlwe
284                    })
285                    .collect_vec();
286                Self { data: rlwes }
287            })
288        }
289    }
290
291    impl<K> Encryptor<[bool], NonInteractiveBatchedFheBools<Mat>> for K
292    where
293        K: Encryptor<[bool], (Mat, [u8; 32])>,
294    {
295        /// Encrypt a vector bool of arbitrary length as vector of unseeded RLWE
296        /// ciphertexts in `NonInteractiveBatchedFheBools`
297        fn encrypt(&self, m: &[bool]) -> NonInteractiveBatchedFheBools<Mat> {
298            NonInteractiveBatchedFheBools::from(&K::encrypt(&self, m))
299        }
300    }
301
302    impl<K> Encryptor<[bool], (Vec<<Mat as Matrix>::R>, [u8; 32])> for K
303    where
304        K: NonInteractiveMultiPartyClientKey,
305        <Mat as Matrix>::R:
306            TryConvertFrom1<[K::Element], CiphertextModulus<<Mat as Matrix>::MatElement>>,
307    {
308        /// Encrypt a vector of bool of arbitrary length as vector of seeded
309        /// RLWE ciphertexts and returns (Vec<RLWE>, Seed)
310        fn encrypt(&self, m: &[bool]) -> (Mat, [u8; 32]) {
311            BoolEvaluator::with_local(|e| {
312                DefaultSecureRng::with_local_mut(|rng| {
313                    let parameters = e.parameters();
314                    let ring_size = parameters.rlwe_n().0;
315
316                    let rlwe_count = ((m.len() as f64 / ring_size as f64).ceil())
317                        .to_usize()
318                        .unwrap();
319
320                    let mut seed = <DefaultSecureRng as NewWithSeed>::Seed::default();
321                    rng.fill_bytes(&mut seed);
322                    let mut prng = DefaultSecureRng::new_seeded(seed);
323
324                    let sk_u = self.sk_u_rlwe();
325
326                    // encrypt `m` into ceil(len(m)/N) RLWE ciphertexts
327                    let rlwes = (0..rlwe_count)
328                        .map(|index| {
329                            let mut message = vec![<Mat as Matrix>::MatElement::zero(); ring_size];
330                            m[(index * ring_size)..std::cmp::min(m.len(), (index + 1) * ring_size)]
331                                .iter()
332                                .enumerate()
333                                .for_each(|(i, v)| {
334                                    if *v {
335                                        message[i] = parameters.rlwe_q().true_el()
336                                    } else {
337                                        message[i] = parameters.rlwe_q().false_el()
338                                    }
339                                });
340
341                            // encrypt message
342                            let mut rlwe_out =
343                                <<Mat as Matrix>::R as RowEntity>::zeros(parameters.rlwe_n().0);
344
345                            seeded_secret_key_encrypt_rlwe(
346                                &message,
347                                &mut rlwe_out,
348                                &sk_u,
349                                e.pbs_info().modop_rlweq(),
350                                e.pbs_info().nttop_rlweq(),
351                                &mut prng,
352                                rng,
353                            );
354
355                            rlwe_out
356                        })
357                        .collect_vec();
358
359                    (rlwes, seed)
360                })
361            })
362        }
363    }
364
365    impl<K> MultiPartyDecryptor<bool, <Mat as Matrix>::R> for K
366    where
367        K: NonInteractiveMultiPartyClientKey,
368        <Mat as Matrix>::R:
369            TryConvertFrom1<[K::Element], CiphertextModulus<<Mat as Matrix>::MatElement>>,
370    {
371        type DecryptionShare = <Mat as Matrix>::MatElement;
372
373        fn gen_decryption_share(&self, c: &<Mat as Matrix>::R) -> Self::DecryptionShare {
374            BoolEvaluator::with_local(|e| {
375                DefaultSecureRng::with_local_mut(|rng| {
376                    multi_party_decryption_share(
377                        c,
378                        self.sk_rlwe().as_slice(),
379                        e.pbs_info().modop_rlweq(),
380                        rng,
381                    )
382                })
383            })
384        }
385
386        fn aggregate_decryption_shares(
387            &self,
388            c: &<Mat as Matrix>::R,
389            shares: &[Self::DecryptionShare],
390        ) -> bool {
391            BoolEvaluator::with_local(|e| {
392                let noisy_m = multi_party_aggregate_decryption_shares_and_decrypt(
393                    c,
394                    shares,
395                    e.pbs_info().modop_rlweq(),
396                );
397
398                e.pbs_info().rlwe_q().decode(noisy_m)
399            })
400        }
401    }
402
403    impl KeySwitchWithId<Mat> for Mat {
404        /// Key switch RLWE ciphertext `Self` from user j's RLWE secret u_j
405        /// to ideal RLWE secret `s` of non-interactive multi-party protocol.
406        ///
407        /// - user_id: user j's user_id in the protocol
408        fn key_switch(&self, user_id: usize) -> Mat {
409            BoolEvaluator::with_local(|e| {
410                assert!(self.dimension() == (2, e.parameters().rlwe_n().0));
411                let server_key = BOOL_SERVER_KEY.get().unwrap();
412                let ksk = server_key.ui_to_s_ksk(user_id);
413                let decomposer = e.ni_ui_to_s_ks_decomposer().as_ref().unwrap();
414
415                // perform key switch
416                rlwe_key_switch(
417                    self,
418                    ksk.as_ref(),
419                    ksk.shoup_repr(),
420                    decomposer,
421                    e.pbs_info().nttop_rlweq(),
422                    e.pbs_info().modop_rlweq(),
423                )
424            })
425        }
426    }
427
428    impl<C> KeySwitchWithId<BatchedFheBools<C>> for NonInteractiveBatchedFheBools<C>
429    where
430        C: KeySwitchWithId<C>,
431    {
432        /// Key switch `Self`'s vector of RLWE ciphertexts from user j's RLWE
433        /// secret u_j to ideal RLWE secret `s` of non-interactive
434        /// multi-party protocol.
435        ///
436        /// Returns vector of key switched RLWE ciphertext as `BatchedFheBools`
437        /// which can then be used to extract individual Bool LWE ciphertexts.
438        ///
439        /// - user_id: user j's user_id in the protocol
440        fn key_switch(&self, user_id: usize) -> BatchedFheBools<C> {
441            let data = self
442                .data
443                .iter()
444                .map(|c| c.key_switch(user_id))
445                .collect_vec();
446            BatchedFheBools { data }
447        }
448    }
449}