arnaucube
arnaucube/poulpyPublic Self-hosted repository
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}