fhe-study/tfhe/src/tgsw.rs
tfhe/src/tgsw.rsRaw
1use anyhow::Result;
2use itertools::zip_eq;
3use rand::Rng;
4use std::ops::Mul;
5
6use arith::{Ring, T64};
7
8use crate::tlev::TLev;
9use crate::tlwe::{SecretKey, TLWE};
10use gfhe::glwe::Param;
11
12/// vector of length K+1 = [K], [1]
13#[derive(Clone, Debug)]
14pub struct TGSW(pub(crate) Vec<TLev>, TLev);
15
16impl TGSW {
17 pub fn encrypt_s(
18 mut rng: impl Rng,
19 param: &Param,
20 beta: u32,
21 l: u32,
22 sk: &SecretKey,
23 m: &T64,
24 ) -> Result<Self> {
25 let a: Vec<TLev> = (0..param.k)
26 .map(|i| TLev::encrypt_s(&mut rng, ¶m, beta, l, sk, &(-sk.0 .0.r[i] * *m)))
27 .collect::<Result<Vec<_>>>()?;
28 let b: TLev = TLev::encrypt_s(&mut rng, ¶m, beta, l, sk, m)?;
29 Ok(Self(a, b))
30 }
31
32 pub fn decrypt(&self, sk: &SecretKey, beta: u32) -> T64 {
33 self.1.decrypt(sk, beta)
34 }
35 pub fn from_tlwe(_tlwe: TLWE) -> Self {
36 todo!()
37 }
38
39 pub fn cmux(bit: Self, ct1: TLWE, ct2: TLWE) -> TLWE {
40 ct1.clone() + (bit * (ct2 - ct1))
41 }
42}
43
44/// External product TGSW x TLWE
45impl Mul<TLWE> for TGSW {
46 type Output = TLWE;
47
48 fn mul(self, tlwe: TLWE) -> TLWE {
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.r.clone(), vec![tlwe.0 .1]].concat();
55
56 let tgsw_ab: Vec<TLev> = [self.0.clone(), vec![self.1]].concat();
57 assert_eq!(tgsw_ab.len(), tlwe_ab.len());
58
59 let r: TLWE = 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 use arith::{RingParam, Rq};
73
74 #[test]
75 fn test_encrypt_decrypt() -> Result<()> {
76 let param = Param {
77 err_sigma: crate::ERR_SIGMA,
78 ring: RingParam { q: u64::MAX, n: 1 },
79 k: 16,
80 t: 2, // plaintext modulus
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, param.t);
87
88 for _ in 0..50 {
89 let (sk, _) = TLWE::new_key(&mut rng, ¶m)?;
90
91 let m: Rq = Rq::rand_u64(&mut rng, msg_dist, ¶m.pt())?;
92 let p: T64 = TLev::encode(¶m, &m); // plaintext
93
94 let c = TGSW::encrypt_s(&mut rng, ¶m, beta, l, &sk, &p)?;
95 let p_recovered = c.decrypt(&sk, beta);
96 let m_recovered = TLev::decode(¶m, &p_recovered);
97
98 assert_eq!(m, m_recovered);
99 }
100
101 Ok(())
102 }
103
104 #[test]
105 fn test_external_product() -> Result<()> {
106 let param = Param {
107 err_sigma: crate::ERR_SIGMA,
108 ring: RingParam { q: u64::MAX, n: 1 },
109 k: 32,
110 t: 2, // plaintext modulus
111 };
112 let beta: u32 = 2;
113 let l: u32 = 64;
114
115 let mut rng = rand::thread_rng();
116 let msg_dist = Uniform::new(0_u64, param.t);
117
118 for _ in 0..50 {
119 let (sk, _) = TLWE::new_key(&mut rng, ¶m)?;
120
121 let m1: Rq = Rq::rand_u64(&mut rng, msg_dist, ¶m.pt())?;
122 let p1: T64 = TLev::encode(¶m, &m1);
123
124 let m2: Rq = Rq::rand_u64(&mut rng, msg_dist, ¶m.pt())?;
125 let p2: T64 = TLWE::encode(¶m, &m2); // scaled by delta
126
127 let tgsw = TGSW::encrypt_s(&mut rng, ¶m, beta, l, &sk, &p1)?;
128 let tlwe = TLWE::encrypt_s(&mut rng, ¶m, &sk, &p2)?;
129
130 let res: TLWE = tgsw * tlwe;
131
132 let p_recovered = res.decrypt(&sk);
133 // downscaled by delta^-1
134 let res_recovered = TLWE::decode(¶m, &p_recovered);
135
136 // assert_eq!(m1 * m2, m_recovered);
137 assert_eq!((m1.to_r() * m2.to_r()).to_rq(param.t), res_recovered);
138 }
139
140 Ok(())
141 }
142
143 #[test]
144 fn test_cmux() -> Result<()> {
145 let param = Param {
146 err_sigma: crate::ERR_SIGMA,
147 ring: RingParam { q: u64::MAX, n: 1 },
148 k: 32,
149 t: 2, // plaintext modulus
150 };
151
152 let beta: u32 = 2;
153 let l: u32 = 64;
154
155 let mut rng = rand::thread_rng();
156 let msg_dist = Uniform::new(0_u64, param.t);
157
158 for _ in 0..50 {
159 let (sk, _) = TLWE::new_key(&mut rng, ¶m)?;
160
161 let m1: Rq = Rq::rand_u64(&mut rng, msg_dist, ¶m.pt())?;
162 let p1: T64 = TLWE::encode(¶m, &m1); // scaled by delta
163
164 let m2: Rq = Rq::rand_u64(&mut rng, msg_dist, ¶m.pt())?;
165 let p2: T64 = TLWE::encode(¶m, &m2); // scaled by delta
166
167 for bit_raw in 0..2 {
168 let bit = TGSW::encrypt_s(&mut rng, ¶m, beta, l, &sk, &T64(bit_raw))?;
169
170 let c1 = TLWE::encrypt_s(&mut rng, ¶m, &sk, &p1)?;
171 let c2 = TLWE::encrypt_s(&mut rng, ¶m, &sk, &p2)?;
172
173 let res: TLWE = TGSW::cmux(bit, c1, c2);
174
175 let p_recovered = res.decrypt(&sk);
176 // downscaled by delta^-1
177 let res_recovered = TLWE::decode(¶m, &p_recovered);
178
179 if bit_raw == 0 {
180 assert_eq!(m1, res_recovered);
181 } else {
182 assert_eq!(m2, res_recovered);
183 }
184 }
185 }
186
187 Ok(())
188 }
189}