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