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