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