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
 11type 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,
312            parameters::{MP_BOOL_PARAMS, SMALL_MP_BOOL_PARAMS, SP_BOOL_PARAMS},
313            set_mp_seed, set_parameter_set,
314        },
315        shortint::types::FheUint8,
316        Decryptor, Encryptor, MultiPartyDecryptor,
317    };
318
319    #[test]
320    fn all_uint8_apis() {
321        set_parameter_set(&SP_BOOL_PARAMS);
322
323        let (ck, sk) = gen_keys();
324        sk.set_server_key();
325
326        for i in 144..=255 {
327            for j in 100..=255 {
328                let m0 = i;
329                let m1 = j;
330                let c0 = ck.encrypt(&m0);
331                let c1 = ck.encrypt(&m1);
332
333                assert!(ck.decrypt(&c0) == m0);
334                assert!(ck.decrypt(&c1) == m1);
335
336                // Arithmetic
337                {
338                    {
339                        // Add
340                        let mut c_m0_plus_m1 = FheUint8 {
341                            data: c0.data().to_vec(),
342                        };
343                        c_m0_plus_m1 += &c1;
344                        let m0_plus_m1 = ck.decrypt(&c_m0_plus_m1);
345                        assert_eq!(
346                            m0_plus_m1,
347                            m0.wrapping_add(m1),
348                            "Expected {} but got {m0_plus_m1} for {i}+{j}",
349                            m0.wrapping_add(m1)
350                        );
351                    }
352                    {
353                        // Sub
354                        let c_sub = &c0 - &c1;
355                        let m0_sub_m1 = ck.decrypt(&c_sub);
356                        assert_eq!(
357                            m0_sub_m1,
358                            m0.wrapping_sub(m1),
359                            "Expected {} but got {m0_sub_m1} for {i}-{j}",
360                            m0.wrapping_sub(m1)
361                        );
362                    }
363
364                    {
365                        // Mul
366                        let c_m0m1 = &c0 * &c1;
367                        let m0m1 = ck.decrypt(&c_m0m1);
368                        assert_eq!(
369                            m0m1,
370                            m0.wrapping_mul(m1),
371                            "Expected {} but got {m0m1} for {i}x{j}",
372                            m0.wrapping_mul(m1)
373                        );
374                    }
375
376                    // Div & Rem
377                    {
378                        let (c_quotient, c_rem) = c0.div_rem(&c1);
379                        let m_quotient = ck.decrypt(&c_quotient);
380                        let m_remainder = ck.decrypt(&c_rem);
381                        if j != 0 {
382                            let (q, r) = i.div_rem_euclid(&j);
383                            assert_eq!(
384                                m_quotient, q,
385                                "Expected {} but got {m_quotient} for {i}/{j}",
386                                q
387                            );
388                            assert_eq!(
389                                m_remainder, r,
390                                "Expected {} but got {m_quotient} for {i}%{j}",
391                                r
392                            );
393                        } else {
394                            assert_eq!(
395                                m_quotient, 255,
396                                "Expected 255 but got {m_quotient}. Case div by zero"
397                            );
398                            assert_eq!(
399                                m_remainder, i,
400                                "Expected {i} but got {m_quotient}. Case div by zero"
401                            )
402                        }
403                    }
404                }
405
406                // Comparisons
407                {
408                    {
409                        let c_eq = c0.eq(&c1);
410                        let is_eq = ck.decrypt(&c_eq);
411                        assert_eq!(
412                            is_eq,
413                            i == j,
414                            "Expected {} but got {is_eq} for {i}=={j}",
415                            i == j
416                        );
417                    }
418
419                    {
420                        let c_gt = c0.gt(&c1);
421                        let is_gt = ck.decrypt(&c_gt);
422                        assert_eq!(
423                            is_gt,
424                            i > j,
425                            "Expected {} but got {is_gt} for {i}>{j}",
426                            i > j
427                        );
428                    }
429
430                    {
431                        let c_lt = c0.lt(&c1);
432                        let is_lt = ck.decrypt(&c_lt);
433                        assert_eq!(
434                            is_lt,
435                            i < j,
436                            "Expected {} but got {is_lt} for {i}<{j}",
437                            i < j
438                        );
439                    }
440
441                    {
442                        let c_ge = c0.ge(&c1);
443                        let is_ge = ck.decrypt(&c_ge);
444                        assert_eq!(
445                            is_ge,
446                            i >= j,
447                            "Expected {} but got {is_ge} for {i}>={j}",
448                            i >= j
449                        );
450                    }
451
452                    {
453                        let c_le = c0.le(&c1);
454                        let is_le = ck.decrypt(&c_le);
455                        assert_eq!(
456                            is_le,
457                            i <= j,
458                            "Expected {} but got {is_le} for {i}<={j}",
459                            i <= j
460                        );
461                    }
462                }
463            }
464        }
465    }
466
467    #[test]
468    fn fheuint8_test_multi_party() {
469        set_parameter_set(&SMALL_MP_BOOL_PARAMS);
470        set_mp_seed([0; 32]);
471
472        let parties = 8;
473
474        // client keys and public key share
475        let cks = (0..parties)
476            .into_iter()
477            .map(|i| gen_client_key())
478            .collect_vec();
479
480        // round 1: generate pulic key shares
481        let pk_shares = cks.iter().map(|key| gen_mp_keys_phase1(key)).collect_vec();
482
483        let public_key = aggregate_public_key_shares(&pk_shares);
484
485        // round 2: generate server key shares
486        let server_key_shares = cks
487            .iter()
488            .map(|key| gen_mp_keys_phase2(key, &public_key))
489            .collect_vec();
490
491        // server aggregates the server key
492        let server_key = aggregate_server_key_shares(&server_key_shares);
493        server_key.set_server_key();
494
495        // Clients use Pk to encrypt private inputs
496        let a = 8u8;
497        let b = 10u8;
498        let c = 155u8;
499        let ct_a = public_key.encrypt(&a);
500        let ct_b = public_key.encrypt(&b);
501        let ct_c = public_key.encrypt(&c);
502
503        let now = std::time::Instant::now();
504        // server computes
505        // a*b + c
506        let mut ct_ab = &ct_a * &ct_b;
507        ct_ab += &ct_c;
508        println!("Circuit time: {:?}", now.elapsed());
509
510        // decrypt ab and check
511        // generate decryption shares
512        let decryption_shares = cks
513            .iter()
514            .map(|k| k.gen_decryption_share(&ct_ab))
515            .collect_vec();
516
517        // aggregate and decryption ab
518        let ab_add_c = cks[0].aggregate_decryption_shares(&ct_ab, &decryption_shares);
519        assert!(ab_add_c == (a.wrapping_mul(b)).wrapping_add(c));
520    }
521}