arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/poulpy/examples/circuit_bootstrapping.rs
poulpy/examples/circuit_bootstrapping.rsRaw
  1use 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 backend::{
 11    hal::{
 12        api::{ModuleNew, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxNormalizeInplace, ZnxView, ZnxViewMut},
 13        layouts::{Module, ScalarZnx, ScratchOwned},
 14    },
 15    implementation::cpu_spqlios::FFT64,
 16};
 17
 18use sampling::source::Source;
 19use schemes::tfhe::{
 20    blind_rotation::CGGI,
 21    circuit_bootstrapping::{
 22        CircuitBootstrappingKey, CircuitBootstrappingKeyEncryptSk, CircuitBootstrappingKeyPrepared, CirtuitBootstrappingExecute,
 23    },
 24};
 25
 26fn main() {
 27    // GLWE ring degree
 28    let n_glwe: usize = 1024;
 29
 30    // Module provides access to the backend arithmetic
 31    let module: Module<FFT64> = Module::<FFT64>::new(n_glwe as u64);
 32
 33    // Base 2 loga
 34    let basek: usize = 13;
 35
 36    // Lookup table extension factor
 37    let extension_factor: usize = 1;
 38
 39    // GLWE rank
 40    let rank: usize = 1;
 41
 42    // Noise (discrete) standard deviation
 43    let sigma: f64 = 3.2;
 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<FFT64> = 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>, FFT64> = 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    module.vec_znx_normalize_inplace(basek, pt_lwe.data_mut(), 0, scratch.borrow());
115
116    println!("pt_lwe: {}", pt_lwe);
117
118    // LWE ciphertext
119    let mut ct_lwe: LWECiphertext<Vec<u8>> = LWECiphertext::alloc(n_lwe, basek, k_lwe_ct);
120
121    // Encrypt LWE Plaintext
122    ct_lwe.encrypt_sk(
123        &module,
124        &pt_lwe,
125        &sk_lwe,
126        &mut source_xa,
127        &mut source_xe,
128        sigma,
129    );
130
131    let now: Instant = Instant::now();
132
133    // Circuit bootstrapping evaluation key
134    let cbt_key: CircuitBootstrappingKey<Vec<u8>, CGGI> = CircuitBootstrappingKey::encrypt_sk(
135        &module,
136        basek,
137        &sk_lwe,
138        &sk_glwe,
139        k_brk,
140        rows_brk,
141        k_trace,
142        rows_trace,
143        k_tsk,
144        rows_tsk,
145        &mut source_xa,
146        &mut source_xe,
147        sigma,
148        scratch.borrow(),
149    );
150    println!("CBT-KGEN: {} ms", now.elapsed().as_millis());
151
152    // Output GGSW
153    let mut res: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(n_glwe, basek, k_ggsw_res, rows_ggsw_res, 1, rank);
154
155    // Circuit bootstrapping key prepared (opaque backend dependant write only struct)
156    let cbt_prepared: CircuitBootstrappingKeyPrepared<Vec<u8>, CGGI, FFT64> = cbt_key.prepare_alloc(&module, scratch.borrow());
157
158    // Apply circuit bootstrapping: LWE(data * 2^{- (k_lwe_pt + 2)}) -> GGSW(data)
159    let now: Instant = Instant::now();
160    cbt_prepared.execute_to_constant(
161        &module,
162        &mut res,
163        &ct_lwe,
164        k_lwe_pt,
165        extension_factor,
166        scratch.borrow(),
167    );
168    println!("CBT: {} ms", now.elapsed().as_millis());
169
170    // Allocate "ideal" GGSW(data) plaintext
171    let mut pt_ggsw: ScalarZnx<Vec<u8>> = ScalarZnx::alloc(n_glwe, 1);
172    pt_ggsw.at_mut(0, 0)[0] = data;
173
174    // Prints noise of GGSW(data)
175    res.print_noise(&module, &sk_glwe_prepared, &pt_ggsw);
176
177    // Tests RLWE(1) * GGSW(data)
178
179    // GLWE ciphertext modulus
180    let mut ct_glwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(n_glwe, basek, k_ggsw_res - basek, rank);
181
182    // Some GLWE plaintext with signed data
183    let k_glwe_pt: usize = 3;
184    let mut pt_glwe: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(n_glwe, basek, basek);
185    let mut data_vec: Vec<i64> = vec![0i64; n_glwe];
186    data_vec
187        .iter_mut()
188        .enumerate()
189        .for_each(|(x, y)| *y = (x % (1 << (k_glwe_pt - 1))) as i64 - (1 << (k_glwe_pt - 2)));
190
191    pt_glwe.encode_vec_i64(&data_vec, k_lwe_pt + 2);
192    pt_glwe.normalize_inplace(&module, scratch.borrow());
193
194    println!("{}", pt_glwe);
195
196    // Encrypt
197    ct_glwe.encrypt_sk(
198        &module,
199        &pt_glwe,
200        &sk_glwe_prepared,
201        &mut source_xa,
202        &mut source_xe,
203        sigma,
204        scratch.borrow(),
205    );
206
207    // Prepare GGSW output of circuit bootstrapping (opaque backend dependant write only struct)
208    let res_prepared: GGSWCiphertextPrepared<Vec<u8>, FFT64> = res.prepare_alloc(&module, scratch.borrow());
209
210    // Apply GLWE x GGSW
211    ct_glwe.external_product_inplace(&module, &res_prepared, scratch.borrow());
212
213    // Decrypt
214    let mut pt_res: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(n_glwe, basek, ct_glwe.k());
215    ct_glwe.decrypt(&module, &mut pt_res, &sk_glwe_prepared, scratch.borrow());
216
217    println!("pt_res: {:?}", &pt_res.data.at(0, 0)[..64]);
218}