arnaucube
arnaucube/phantom-zonePublic Self-hosted repository
phantom-zone/src/shortint/mod.rs
src/shortint/mod.rsRaw
  1use itertools::Itertools;
  2
  3use crate::{
  4    bool::keys::{ClientKey, PublicKey},
  5    Decryptor, Encryptor, MultiPartyDecryptor,
  6};
  7
  8mod ops;
  9mod types;
 10
 11pub type FheUint8 = types::FheUint8<Vec<u64>>;
 12
 13impl Encryptor<u8, FheUint8> for ClientKey {
 14    fn encrypt(&self, m: &u8) -> FheUint8 {
 15        let cts = (0..8)
 16            .into_iter()
 17            .map(|i| {
 18                let bit = ((m >> i) & 1) == 1;
 19                Encryptor::<bool, Vec<u64>>::encrypt(self, &bit)
 20            })
 21            .collect_vec();
 22        FheUint8 { data: cts }
 23    }
 24}
 25
 26impl Decryptor<u8, FheUint8> for ClientKey {
 27    fn decrypt(&self, c: &FheUint8) -> u8 {
 28        assert!(c.data.len() == 8);
 29        let mut out = 0u8;
 30        c.data().iter().enumerate().for_each(|(index, bit_c)| {
 31            let bool = Decryptor::<bool, Vec<u64>>::decrypt(self, bit_c);
 32            if bool {
 33                out += 1 << index;
 34            }
 35        });
 36        out
 37    }
 38}
 39
 40impl<M, R, Mo> Encryptor<u8, FheUint8> for PublicKey<M, R, Mo>
 41where
 42    PublicKey<M, R, Mo>: Encryptor<bool, Vec<u64>>,
 43{
 44    fn encrypt(&self, m: &u8) -> FheUint8 {
 45        let cts = (0..8)
 46            .into_iter()
 47            .map(|i| {
 48                let bit = ((m >> i) & 1) == 1;
 49                Encryptor::<bool, Vec<u64>>::encrypt(self, &bit)
 50            })
 51            .collect_vec();
 52        FheUint8 { data: cts }
 53    }
 54}
 55
 56impl MultiPartyDecryptor<u8, FheUint8> for ClientKey
 57where
 58    ClientKey: MultiPartyDecryptor<bool, Vec<u64>>,
 59{
 60    type DecryptionShare = Vec<<Self as MultiPartyDecryptor<bool, Vec<u64>>>::DecryptionShare>;
 61    fn gen_decryption_share(&self, c: &FheUint8) -> Self::DecryptionShare {
 62        assert!(c.data().len() == 8);
 63        c.data()
 64            .iter()
 65            .map(|bit_c| {
 66                let decryption_share =
 67                    MultiPartyDecryptor::<bool, Vec<u64>>::gen_decryption_share(self, bit_c);
 68                decryption_share
 69            })
 70            .collect_vec()
 71    }
 72
 73    fn aggregate_decryption_shares(&self, c: &FheUint8, shares: &[Self::DecryptionShare]) -> u8 {
 74        let mut out = 0u8;
 75
 76        (0..8).into_iter().for_each(|i| {
 77            // Collect bit i^th decryption share of each party
 78            let bit_i_decryption_shares = shares.iter().map(|s| s[i]).collect_vec();
 79            let bit_i = MultiPartyDecryptor::<bool, Vec<u64>>::aggregate_decryption_shares(
 80                self,
 81                &c.data()[i],
 82                &bit_i_decryption_shares,
 83            );
 84
 85            if bit_i {
 86                out += 1 << i;
 87            }
 88        });
 89
 90        out
 91    }
 92}
 93
 94mod frontend {
 95    use super::ops::{
 96        arbitrary_bit_adder, arbitrary_bit_division_for_quotient_and_rem, arbitrary_bit_subtractor,
 97        eight_bit_mul,
 98    };
 99    use crate::{
100        bool::{
101            evaluator::{self, BoolEvaluator, BooleanGates},
102            keys::{ServerKeyEvaluationDomain, ShoupServerKeyEvaluationDomain},
103        },
104        utils::{Global, WithLocal},
105    };
106
107    use super::FheUint8;
108
109    mod arithetic {
110        use crate::bool::{FheBool, RuntimeServerKey};
111
112        use super::*;
113        use std::ops::{Add, AddAssign, Div, Mul, Rem, Sub};
114
115        impl AddAssign<&FheUint8> for FheUint8 {
116            fn add_assign(&mut self, rhs: &FheUint8) {
117                BoolEvaluator::with_local_mut_mut(&mut |e| {
118                    let key = RuntimeServerKey::global();
119                    arbitrary_bit_adder(e, self.data_mut(), rhs.data(), false, key);
120                });
121            }
122        }
123
124        impl Add<&FheUint8> for &FheUint8 {
125            type Output = FheUint8;
126            fn add(self, rhs: &FheUint8) -> Self::Output {
127                let mut a = self.clone();
128                a += rhs;
129                a
130            }
131        }
132
133        impl Sub<&FheUint8> for &FheUint8 {
134            type Output = FheUint8;
135            fn sub(self, rhs: &FheUint8) -> Self::Output {
136                BoolEvaluator::with_local_mut(|e| {
137                    let key = RuntimeServerKey::global();
138                    let (out, _, _) = arbitrary_bit_subtractor(e, self.data(), rhs.data(), key);
139                    FheUint8 { data: out }
140                })
141            }
142        }
143
144        impl Mul<&FheUint8> for &FheUint8 {
145            type Output = FheUint8;
146            fn mul(self, rhs: &FheUint8) -> Self::Output {
147                BoolEvaluator::with_local_mut(|e| {
148                    let key = RuntimeServerKey::global();
149                    let out = eight_bit_mul(e, self.data(), rhs.data(), key);
150                    FheUint8 { data: out }
151                })
152            }
153        }
154
155        impl Div<&FheUint8> for &FheUint8 {
156            type Output = FheUint8;
157            fn div(self, rhs: &FheUint8) -> Self::Output {
158                // TODO(Jay:) Figure out how to set zero error flag
159                BoolEvaluator::with_local_mut(|e| {
160                    let key = RuntimeServerKey::global();
161                    let (quotient, _) = arbitrary_bit_division_for_quotient_and_rem(
162                        e,
163                        self.data(),
164                        rhs.data(),
165                        key,
166                    );
167                    FheUint8 { data: quotient }
168                })
169            }
170        }
171
172        impl Rem<&FheUint8> for &FheUint8 {
173            type Output = FheUint8;
174            fn rem(self, rhs: &FheUint8) -> Self::Output {
175                BoolEvaluator::with_local_mut(|e| {
176                    let key = RuntimeServerKey::global();
177                    let (_, remainder) = arbitrary_bit_division_for_quotient_and_rem(
178                        e,
179                        self.data(),
180                        rhs.data(),
181                        key,
182                    );
183                    FheUint8 { data: remainder }
184                })
185            }
186        }
187
188        impl FheUint8 {
189            pub fn overflowing_add_assign(&mut self, rhs: &FheUint8) -> FheBool {
190                BoolEvaluator::with_local_mut_mut(&mut |e| {
191                    let key = RuntimeServerKey::global();
192                    let (overflow, _) =
193                        arbitrary_bit_adder(e, self.data_mut(), rhs.data(), false, key);
194                    overflow
195                })
196            }
197
198            pub fn overflowing_add(self, rhs: &FheUint8) -> (FheUint8, FheBool) {
199                BoolEvaluator::with_local_mut(|e| {
200                    let mut lhs = self.clone();
201                    let key = RuntimeServerKey::global();
202                    let (overflow, _) =
203                        arbitrary_bit_adder(e, lhs.data_mut(), rhs.data(), false, key);
204                    (lhs, overflow)
205                })
206            }
207
208            pub fn overflowing_sub(&self, rhs: &FheUint8) -> (FheUint8, FheBool) {
209                BoolEvaluator::with_local_mut(|e| {
210                    let key = RuntimeServerKey::global();
211                    let (out, mut overflow, _) =
212                        arbitrary_bit_subtractor(e, self.data(), rhs.data(), key);
213                    e.not_inplace(&mut overflow);
214                    (FheUint8 { data: out }, overflow)
215                })
216            }
217
218            pub fn div_rem(&self, rhs: &FheUint8) -> (FheUint8, FheUint8) {
219                // TODO(Jay:) Figure out how to set zero error flag
220                BoolEvaluator::with_local_mut(|e| {
221                    let key = RuntimeServerKey::global();
222                    let (quotient, remainder) = arbitrary_bit_division_for_quotient_and_rem(
223                        e,
224                        self.data(),
225                        rhs.data(),
226                        key,
227                    );
228                    (FheUint8 { data: quotient }, FheUint8 { data: remainder })
229                })
230            }
231        }
232    }
233
234    mod booleans {
235        use crate::{
236            bool::{evaluator::BooleanGates, FheBool, RuntimeServerKey},
237            shortint::ops::{
238                arbitrary_bit_comparator, arbitrary_bit_equality, arbitrary_signed_bit_comparator,
239            },
240        };
241
242        use super::*;
243
244        impl FheUint8 {
245            /// a == b
246            pub fn eq(&self, other: &FheUint8) -> FheBool {
247                BoolEvaluator::with_local_mut(|e| {
248                    let key = RuntimeServerKey::global();
249                    arbitrary_bit_equality(e, self.data(), other.data(), key)
250                })
251            }
252
253            /// a != b
254            pub fn neq(&self, other: &FheUint8) -> FheBool {
255                BoolEvaluator::with_local_mut(|e| {
256                    let key = RuntimeServerKey::global();
257                    let mut is_equal = arbitrary_bit_equality(e, self.data(), other.data(), key);
258                    e.not_inplace(&mut is_equal);
259                    is_equal
260                })
261            }
262
263            /// a < b
264            pub fn lt(&self, other: &FheUint8) -> FheBool {
265                BoolEvaluator::with_local_mut(|e| {
266                    let key = RuntimeServerKey::global();
267                    arbitrary_bit_comparator(e, other.data(), self.data(), key)
268                })
269            }
270
271            /// a > b
272            pub fn gt(&self, other: &FheUint8) -> FheBool {
273                BoolEvaluator::with_local_mut(|e| {
274                    let key = RuntimeServerKey::global();
275                    arbitrary_bit_comparator(e, self.data(), other.data(), key)
276                })
277            }
278
279            /// a <= b
280            pub fn le(&self, other: &FheUint8) -> FheBool {
281                BoolEvaluator::with_local_mut(|e| {
282                    let key = RuntimeServerKey::global();
283                    let mut a_greater_b =
284                        arbitrary_bit_comparator(e, self.data(), other.data(), key);
285                    e.not_inplace(&mut a_greater_b);
286                    a_greater_b
287                })
288            }
289
290            /// a >= b
291            pub fn ge(&self, other: &FheUint8) -> FheBool {
292                BoolEvaluator::with_local_mut(|e| {
293                    let key = RuntimeServerKey::global();
294                    let mut a_less_b = arbitrary_bit_comparator(e, other.data(), self.data(), key);
295                    e.not_inplace(&mut a_less_b);
296                    a_less_b
297                })
298            }
299        }
300    }
301}
302
303#[cfg(test)]
304mod tests {
305    use itertools::Itertools;
306    use num_traits::Euclid;
307
308    use crate::{
309        bool::{
310            aggregate_public_key_shares, aggregate_server_key_shares, gen_client_key, gen_keys,
311            gen_mp_keys_phase1, gen_mp_keys_phase2, set_mp_seed, set_parameter_set,
312        },
313        shortint::types::FheUint8,
314        Decryptor, Encryptor, MultiPartyDecryptor,
315    };
316
317    #[test]
318    fn all_uint8_apis() {
319        set_parameter_set(crate::ParameterSelector::MultiPartyLessThan16);
320
321        let (ck, sk) = gen_keys();
322        sk.set_server_key();
323
324        for i in 144..=255 {
325            for j in 100..=255 {
326                let m0 = i;
327                let m1 = j;
328                let c0 = ck.encrypt(&m0);
329                let c1 = ck.encrypt(&m1);
330
331                assert!(ck.decrypt(&c0) == m0);
332                assert!(ck.decrypt(&c1) == m1);
333
334                // Arithmetic
335                {
336                    {
337                        // Add
338                        let mut c_m0_plus_m1 = FheUint8 {
339                            data: c0.data().to_vec(),
340                        };
341                        c_m0_plus_m1 += &c1;
342                        let m0_plus_m1 = ck.decrypt(&c_m0_plus_m1);
343                        assert_eq!(
344                            m0_plus_m1,
345                            m0.wrapping_add(m1),
346                            "Expected {} but got {m0_plus_m1} for {i}+{j}",
347                            m0.wrapping_add(m1)
348                        );
349                    }
350                    {
351                        // Sub
352                        let c_sub = &c0 - &c1;
353                        let m0_sub_m1 = ck.decrypt(&c_sub);
354                        assert_eq!(
355                            m0_sub_m1,
356                            m0.wrapping_sub(m1),
357                            "Expected {} but got {m0_sub_m1} for {i}-{j}",
358                            m0.wrapping_sub(m1)
359                        );
360                    }
361
362                    {
363                        // Mul
364                        let c_m0m1 = &c0 * &c1;
365                        let m0m1 = ck.decrypt(&c_m0m1);
366                        assert_eq!(
367                            m0m1,
368                            m0.wrapping_mul(m1),
369                            "Expected {} but got {m0m1} for {i}x{j}",
370                            m0.wrapping_mul(m1)
371                        );
372                    }
373
374                    // Div & Rem
375                    {
376                        let (c_quotient, c_rem) = c0.div_rem(&c1);
377                        let m_quotient = ck.decrypt(&c_quotient);
378                        let m_remainder = ck.decrypt(&c_rem);
379                        if j != 0 {
380                            let (q, r) = i.div_rem_euclid(&j);
381                            assert_eq!(
382                                m_quotient, q,
383                                "Expected {} but got {m_quotient} for {i}/{j}",
384                                q
385                            );
386                            assert_eq!(
387                                m_remainder, r,
388                                "Expected {} but got {m_quotient} for {i}%{j}",
389                                r
390                            );
391                        } else {
392                            assert_eq!(
393                                m_quotient, 255,
394                                "Expected 255 but got {m_quotient}. Case div by zero"
395                            );
396                            assert_eq!(
397                                m_remainder, i,
398                                "Expected {i} but got {m_quotient}. Case div by zero"
399                            )
400                        }
401                    }
402                }
403
404                // Comparisons
405                {
406                    {
407                        let c_eq = c0.eq(&c1);
408                        let is_eq = ck.decrypt(&c_eq);
409                        assert_eq!(
410                            is_eq,
411                            i == j,
412                            "Expected {} but got {is_eq} for {i}=={j}",
413                            i == j
414                        );
415                    }
416
417                    {
418                        let c_gt = c0.gt(&c1);
419                        let is_gt = ck.decrypt(&c_gt);
420                        assert_eq!(
421                            is_gt,
422                            i > j,
423                            "Expected {} but got {is_gt} for {i}>{j}",
424                            i > j
425                        );
426                    }
427
428                    {
429                        let c_lt = c0.lt(&c1);
430                        let is_lt = ck.decrypt(&c_lt);
431                        assert_eq!(
432                            is_lt,
433                            i < j,
434                            "Expected {} but got {is_lt} for {i}<{j}",
435                            i < j
436                        );
437                    }
438
439                    {
440                        let c_ge = c0.ge(&c1);
441                        let is_ge = ck.decrypt(&c_ge);
442                        assert_eq!(
443                            is_ge,
444                            i >= j,
445                            "Expected {} but got {is_ge} for {i}>={j}",
446                            i >= j
447                        );
448                    }
449
450                    {
451                        let c_le = c0.le(&c1);
452                        let is_le = ck.decrypt(&c_le);
453                        assert_eq!(
454                            is_le,
455                            i <= j,
456                            "Expected {} but got {is_le} for {i}<={j}",
457                            i <= j
458                        );
459                    }
460                }
461            }
462        }
463    }
464
465    #[test]
466    fn fheuint8_test_multi_party() {
467        set_parameter_set(crate::ParameterSelector::MultiPartyLessThan16);
468        set_mp_seed([0; 32]);
469
470        let parties = 8;
471
472        // client keys and public key share
473        let cks = (0..parties)
474            .into_iter()
475            .map(|i| gen_client_key())
476            .collect_vec();
477
478        // round 1: generate pulic key shares
479        let pk_shares = cks.iter().map(|key| gen_mp_keys_phase1(key)).collect_vec();
480
481        let public_key = aggregate_public_key_shares(&pk_shares);
482
483        // round 2: generate server key shares
484        let server_key_shares = cks
485            .iter()
486            .map(|key| gen_mp_keys_phase2(key, &public_key))
487            .collect_vec();
488
489        // server aggregates the server key
490        let server_key = aggregate_server_key_shares(&server_key_shares);
491        server_key.set_server_key();
492
493        // Clients use Pk to encrypt private inputs
494        let a = 8u8;
495        let b = 10u8;
496        let c = 155u8;
497        let ct_a = public_key.encrypt(&a);
498        let ct_b = public_key.encrypt(&b);
499        let ct_c = public_key.encrypt(&c);
500
501        let now = std::time::Instant::now();
502        // server computes
503        // a*b + c
504        let mut ct_ab = &ct_a * &ct_b;
505        ct_ab += &ct_c;
506        println!("Circuit time: {:?}", now.elapsed());
507
508        // decrypt ab and check
509        // generate decryption shares
510        let decryption_shares = cks
511            .iter()
512            .map(|k| k.gen_decryption_share(&ct_ab))
513            .collect_vec();
514
515        // aggregate and decryption ab
516        let ab_add_c = cks[0].aggregate_decryption_shares(&ct_ab, &decryption_shares);
517        assert!(ab_add_c == (a.wrapping_mul(b)).wrapping_add(c));
518    }
519}