poulpy/poulpy-schemes/examples/circuit_bootstrapping.rs
poulpy-schemes/examples/circuit_bootstrapping.rsRaw
1use poulpy_core::{
2 GLWEOperations,
3 layouts::{
4 GGSWCiphertext, GLWECiphertext, GLWEPlaintext, GLWESecret, Infos, LWECiphertext, LWEPlaintext, LWESecret,
5 prepared::{GGSWCiphertextPrepared, GLWESecretPrepared, PrepareAlloc},
6 },
7};
8use std::time::Instant;
9
10use poulpy_hal::{
11 api::{ModuleNew, ScratchOwnedAlloc, ScratchOwnedBorrow, ZnNormalizeInplace},
12 layouts::{Module, ScalarZnx, ScratchOwned, ZnxView, ZnxViewMut},
13 source::Source,
14};
15
16use poulpy_backend::{FFT64Avx};
17
18use poulpy_schemes::tfhe::{
19 blind_rotation::CGGI,
20 circuit_bootstrapping::{
21 CircuitBootstrappingKey, CircuitBootstrappingKeyEncryptSk, CircuitBootstrappingKeyPrepared, CirtuitBootstrappingExecute,
22 },
23};
24
25fn main() {
26 // GLWE ring degree
27 let n_glwe: usize = 1024;
28
29 // Module provides access to the backend arithmetic
30 let module: Module<FFT64Avx> = Module::<FFT64Avx>::new(n_glwe as u64);
31
32 // Base 2 loga
33 let basek: usize = 13;
34
35 // Lookup table extension factor
36 let extension_factor: usize = 1;
37
38 // GLWE rank
39 let rank: usize = 1;
40
41 // LWE degree
42 let n_lwe: usize = 574;
43
44 // LWE plaintext modulus
45 let k_lwe_pt: usize = 1;
46
47 // LWE ciphertext modulus
48 let k_lwe_ct: usize = 13;
49
50 // LWE block binary key block size
51 let block_size: usize = 7;
52
53 // GGSW output number of rows
54 let rows_ggsw_res: usize = 2;
55
56 // GGSW output modulus
57 let k_ggsw_res: usize = (rows_ggsw_res + 1) * basek;
58
59 // Blind rotation key GGSW number of rows
60 let rows_brk: usize = rows_ggsw_res + 1;
61
62 // Blind rotation key GGSW modulus
63 let k_brk: usize = (rows_brk + 1) * basek;
64
65 // GGLWE automorphism keys number of rows
66 let rows_trace: usize = rows_ggsw_res + 1;
67
68 // GGLWE automorphism keys modulus
69 let k_trace: usize = (rows_trace + 1) * basek;
70
71 // GGLWE tensor key number of rows
72 let rows_tsk: usize = rows_ggsw_res + 1;
73
74 // GGLWE tensor key modulus
75 let k_tsk: usize = (rows_tsk + 1) * basek;
76
77 // Scratch space (4MB)
78 let mut scratch: ScratchOwned<FFT64Avx> = ScratchOwned::alloc(1 << 22);
79
80 // Secret key sampling source
81 let mut source_xs: Source = Source::new([1u8; 32]);
82
83 // Public randomness sampling source
84 let mut source_xa: Source = Source::new([1u8; 32]);
85
86 // Noise sampling source
87 let mut source_xe: Source = Source::new([1u8; 32]);
88
89 // LWE secret
90 let mut sk_lwe: LWESecret<Vec<u8>> = LWESecret::alloc(n_lwe);
91 sk_lwe.fill_binary_block(block_size, &mut source_xs);
92 sk_lwe.fill_zero();
93
94 // GLWE secret
95 let mut sk_glwe: GLWESecret<Vec<u8>> = GLWESecret::alloc(n_glwe, rank);
96 sk_glwe.fill_ternary_prob(0.5, &mut source_xs);
97 // sk_glwe.fill_zero();
98
99 // GLWE secret prepared (opaque backend dependant write only struct)
100 let sk_glwe_prepared: GLWESecretPrepared<Vec<u8>, FFT64Avx> = sk_glwe.prepare_alloc(&module, scratch.borrow());
101
102 // Plaintext value to circuit bootstrap
103 let data: i64 = 1 % (1 << k_lwe_pt);
104
105 // LWE plaintext
106 let mut pt_lwe: LWEPlaintext<Vec<u8>> = LWEPlaintext::alloc(basek, k_lwe_pt);
107
108 // LWE plaintext(data * 2^{- (k_lwe_pt - 1)})
109 pt_lwe.encode_i64(data, k_lwe_pt + 1); // +1 for padding bit
110
111 // Normalize plaintext to nicely print coefficients
112 module.zn_normalize_inplace(pt_lwe.n(), basek, pt_lwe.data_mut(), 0, scratch.borrow());
113 println!("pt_lwe: {}", pt_lwe);
114
115 // LWE ciphertext
116 let mut ct_lwe: LWECiphertext<Vec<u8>> = LWECiphertext::alloc(n_lwe, basek, k_lwe_ct);
117
118 // Encrypt LWE Plaintext
119 ct_lwe.encrypt_sk(&module, &pt_lwe, &sk_lwe, &mut source_xa, &mut source_xe);
120
121 let now: Instant = Instant::now();
122
123 // Circuit bootstrapping evaluation key
124 let cbt_key: CircuitBootstrappingKey<Vec<u8>, CGGI> = CircuitBootstrappingKey::encrypt_sk(
125 &module,
126 basek,
127 &sk_lwe,
128 &sk_glwe,
129 k_brk,
130 rows_brk,
131 k_trace,
132 rows_trace,
133 k_tsk,
134 rows_tsk,
135 &mut source_xa,
136 &mut source_xe,
137 scratch.borrow(),
138 );
139 println!("CBT-KGEN: {} ms", now.elapsed().as_millis());
140
141 // Output GGSW
142 let mut res: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(n_glwe, basek, k_ggsw_res, rows_ggsw_res, 1, rank);
143
144 // Circuit bootstrapping key prepared (opaque backend dependant write only struct)
145 let cbt_prepared: CircuitBootstrappingKeyPrepared<Vec<u8>, CGGI, FFT64Avx> =
146 cbt_key.prepare_alloc(&module, scratch.borrow());
147
148 // Apply circuit bootstrapping: LWE(data * 2^{- (k_lwe_pt + 2)}) -> GGSW(data)
149 let now: Instant = Instant::now();
150 cbt_prepared.execute_to_constant(
151 &module,
152 &mut res,
153 &ct_lwe,
154 k_lwe_pt,
155 extension_factor,
156 scratch.borrow(),
157 );
158 println!("CBT: {} ms", now.elapsed().as_millis());
159
160 // Allocate "ideal" GGSW(data) plaintext
161 let mut pt_ggsw: ScalarZnx<Vec<u8>> = ScalarZnx::alloc(n_glwe, 1);
162 pt_ggsw.at_mut(0, 0)[0] = data;
163
164 // Prints noise of GGSW(data)
165 res.print_noise(&module, &sk_glwe_prepared, &pt_ggsw);
166
167 // Tests RLWE(1) * GGSW(data)
168
169 // GLWE ciphertext modulus
170 let mut ct_glwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(n_glwe, basek, k_ggsw_res - basek, rank);
171
172 // Some GLWE plaintext with signed data
173 let k_glwe_pt: usize = 3;
174 let mut pt_glwe: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(n_glwe, basek, basek);
175 let mut data_vec: Vec<i64> = vec![0i64; n_glwe];
176 data_vec
177 .iter_mut()
178 .enumerate()
179 .for_each(|(x, y)| *y = (x % (1 << (k_glwe_pt - 1))) as i64 - (1 << (k_glwe_pt - 2)));
180
181 pt_glwe.encode_vec_i64(&data_vec, k_lwe_pt + 2);
182 pt_glwe.normalize_inplace(&module, scratch.borrow());
183
184 println!("{}", pt_glwe);
185
186 // Encrypt
187 ct_glwe.encrypt_sk(
188 &module,
189 &pt_glwe,
190 &sk_glwe_prepared,
191 &mut source_xa,
192 &mut source_xe,
193 scratch.borrow(),
194 );
195
196 // Prepare GGSW output of circuit bootstrapping (opaque backend dependant write only struct)
197 let res_prepared: GGSWCiphertextPrepared<Vec<u8>, FFT64Avx> = res.prepare_alloc(&module, scratch.borrow());
198
199 // Apply GLWE x GGSW
200 ct_glwe.external_product_inplace(&module, &res_prepared, scratch.borrow());
201
202 // Decrypt
203 let mut pt_res: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(n_glwe, basek, ct_glwe.k());
204 ct_glwe.decrypt(&module, &mut pt_res, &sk_glwe_prepared, scratch.borrow());
205
206 println!("pt_res: {:?}", &pt_res.data.at(0, 0)[..64]);
207}