arnaucube
arnaucube/fhe-studyPublic Self-hosted repository
fhe-study/tfhe/src/tgsw.rs
tfhe/src/tgsw.rsRaw
  1use anyhow::Result;
  2use itertools::zip_eq;
  3use rand::Rng;
  4use std::array;
  5use std::ops::{Add, Mul};
  6
  7use arith::{Ring, Rq, Tn, T64, TR};
  8
  9use crate::tlev::TLev;
 10use crate::tlwe::{PublicKey, SecretKey, TLWE};
 11use gfhe::glwe::GLWE;
 12
 13/// vector of length K+1 = [K], [1]
 14#[derive(Clone, Debug)]
 15pub struct TGSW<const K: usize>(pub(crate) Vec<TLev<K>>, TLev<K>);
 16
 17impl<const K: usize> TGSW<K> {
 18    pub fn encrypt_s(
 19        mut rng: impl Rng,
 20        beta: u32,
 21        l: u32,
 22        sk: &SecretKey<K>,
 23        m: &T64,
 24    ) -> Result<Self> {
 25        let a: Vec<TLev<K>> = (0..K)
 26            .map(|i| TLev::encrypt_s(&mut rng, beta, l, sk, &(-sk.0 .0[i] * *m)))
 27            .collect::<Result<Vec<_>>>()?;
 28        let b: TLev<K> = TLev::encrypt_s(&mut rng, beta, l, sk, m)?;
 29        Ok(Self(a, b))
 30    }
 31
 32    pub fn decrypt(&self, sk: &SecretKey<K>, beta: u32) -> T64 {
 33        self.1.decrypt(sk, beta)
 34    }
 35    pub fn from_tlwe(_tlwe: TLWE<K>) -> Self {
 36        todo!()
 37    }
 38
 39    pub fn cmux(bit: Self, ct1: TLWE<K>, ct2: TLWE<K>) -> TLWE<K> {
 40        ct1.clone() + (bit * (ct2 - ct1))
 41    }
 42}
 43
 44/// External product TGSW x TLWE
 45impl<const K: usize> Mul<TLWE<K>> for TGSW<K> {
 46    type Output = TLWE<K>;
 47
 48    fn mul(self, tlwe: TLWE<K>) -> TLWE<K> {
 49        let beta: u32 = 2;
 50        let l: u32 = 64; // TODO wip
 51
 52        // since N=1, each tlwe element is a vector of length=1, decomposed into
 53        // l elements, and we have K of them
 54        let tlwe_ab: Vec<T64> = [tlwe.0 .0 .0.clone(), vec![tlwe.0 .1]].concat();
 55
 56        let tgsw_ab: Vec<TLev<K>> = [self.0.clone(), vec![self.1]].concat();
 57        assert_eq!(tgsw_ab.len(), tlwe_ab.len());
 58
 59        let r: TLWE<K> = zip_eq(tgsw_ab, tlwe_ab)
 60            .map(|(tlev_i, tlwe_i)| tlev_i * tlwe_i.decompose(beta, l))
 61            .sum();
 62        r
 63    }
 64}
 65
 66#[cfg(test)]
 67mod tests {
 68    use anyhow::Result;
 69    use rand::distributions::Uniform;
 70
 71    use super::*;
 72
 73    #[test]
 74    fn test_encrypt_decrypt() -> Result<()> {
 75        const T: u64 = 2; // plaintext modulus
 76        const K: usize = 16;
 77        type S = TGSW<K>;
 78
 79        let beta: u32 = 2;
 80        let l: u32 = 16;
 81
 82        let mut rng = rand::thread_rng();
 83        let msg_dist = Uniform::new(0_u64, T);
 84
 85        for _ in 0..50 {
 86            let (sk, _) = TLWE::<K>::new_key(&mut rng)?;
 87
 88            let m: Rq<T, 1> = Rq::rand_u64(&mut rng, msg_dist)?;
 89            let p: T64 = TLev::<K>::encode::<T>(&m); // plaintext
 90
 91            let c = S::encrypt_s(&mut rng, beta, l, &sk, &p)?;
 92            let p_recovered = c.decrypt(&sk, beta);
 93            let m_recovered = TLev::<K>::decode::<T>(&p_recovered);
 94
 95            assert_eq!(m, m_recovered);
 96        }
 97
 98        Ok(())
 99    }
100
101    #[test]
102    fn test_external_product() -> Result<()> {
103        const T: u64 = 2; // plaintext modulus
104        const K: usize = 32;
105
106        let beta: u32 = 2;
107        let l: u32 = 64;
108
109        let mut rng = rand::thread_rng();
110        let msg_dist = Uniform::new(0_u64, T);
111
112        for _ in 0..50 {
113            let (sk, _) = TLWE::<K>::new_key(&mut rng)?;
114
115            let m1: Rq<T, 1> = Rq::rand_u64(&mut rng, msg_dist)?;
116            let p1: T64 = TLev::<K>::encode::<T>(&m1);
117
118            let m2: Rq<T, 1> = Rq::rand_u64(&mut rng, msg_dist)?;
119            let p2: T64 = TLWE::<K>::encode::<T>(&m2); // scaled by delta
120
121            let tgsw = TGSW::<K>::encrypt_s(&mut rng, beta, l, &sk, &p1)?;
122            let tlwe = TLWE::<K>::encrypt_s(&mut rng, &sk, &p2)?;
123
124            let res: TLWE<K> = tgsw * tlwe;
125
126            // let p_recovered = res.decrypt(&sk, beta);
127            let p_recovered = res.decrypt(&sk);
128            // downscaled by delta^-1
129            let res_recovered = TLWE::<K>::decode::<T>(&p_recovered);
130
131            // assert_eq!(m1 * m2, m_recovered);
132            assert_eq!((m1.to_r() * m2.to_r()).to_rq::<T>(), res_recovered);
133        }
134
135        Ok(())
136    }
137
138    #[test]
139    fn test_cmux() -> Result<()> {
140        const T: u64 = 2; // plaintext modulus
141        const K: usize = 32;
142
143        let beta: u32 = 2;
144        let l: u32 = 64;
145
146        let mut rng = rand::thread_rng();
147        let msg_dist = Uniform::new(0_u64, T);
148
149        for _ in 0..50 {
150            let (sk, _) = TLWE::<K>::new_key(&mut rng)?;
151
152            let m1: Rq<T, 1> = Rq::rand_u64(&mut rng, msg_dist)?;
153            let p1: T64 = TLWE::<K>::encode::<T>(&m1); // scaled by delta
154
155            let m2: Rq<T, 1> = Rq::rand_u64(&mut rng, msg_dist)?;
156            let p2: T64 = TLWE::<K>::encode::<T>(&m2); // scaled by delta
157
158            for bit_raw in 0..2 {
159                let bit = TGSW::<K>::encrypt_s(&mut rng, beta, l, &sk, &T64(bit_raw))?;
160
161                let c1 = TLWE::<K>::encrypt_s(&mut rng, &sk, &p1)?;
162                let c2 = TLWE::<K>::encrypt_s(&mut rng, &sk, &p2)?;
163
164                let res: TLWE<K> = TGSW::cmux(bit, c1, c2);
165
166                let p_recovered = res.decrypt(&sk);
167                // downscaled by delta^-1
168                let res_recovered = TLWE::<K>::decode::<T>(&p_recovered);
169
170                if bit_raw == 0 {
171                    assert_eq!(m1, res_recovered);
172                } else {
173                    assert_eq!(m2, res_recovered);
174                }
175            }
176        }
177
178        Ok(())
179    }
180}