poulpy/schemes/src/tfhe/blind_rotation/tests/generic_blind_rotation.rs
schemes/src/tfhe/blind_rotation/tests/generic_blind_rotation.rsRaw
1use backend::hal::{
2 api::{
3 ScratchOwnedAlloc, ScratchOwnedBorrow, SvpApply, SvpApplyInplace, SvpPPolAlloc, SvpPPolAllocBytes, SvpPrepare,
4 VecZnxAddInplace, VecZnxAddNormal, VecZnxAddScalarInplace, VecZnxBigAddInplace, VecZnxBigAddSmallInplace,
5 VecZnxBigAllocBytes, VecZnxBigNormalize, VecZnxBigNormalizeTmpBytes, VecZnxCopy, VecZnxDftAdd, VecZnxDftAddInplace,
6 VecZnxDftAllocBytes, VecZnxDftFromVecZnx, VecZnxDftSubABInplace, VecZnxDftToVecZnxBig, VecZnxDftToVecZnxBigConsume,
7 VecZnxDftToVecZnxBigTmpBytes, VecZnxDftZero, VecZnxFillUniform, VecZnxMulXpMinusOneInplace, VecZnxNormalize,
8 VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxRotate, VecZnxRotateInplace, VecZnxSub, VecZnxSubABInplace,
9 VecZnxSwithcDegree, VmpApply, VmpApplyAdd, VmpApplyTmpBytes, VmpPMatAlloc, VmpPrepare, ZnxView,
10 },
11 layouts::{Backend, Module, ScratchOwned},
12 oep::{
13 ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeVecZnxBigImpl, TakeVecZnxDftImpl,
14 TakeVecZnxDftSliceImpl, TakeVecZnxImpl, TakeVecZnxSliceImpl, VecZnxBigAllocBytesImpl, VecZnxDftAllocBytesImpl,
15 },
16};
17use sampling::source::Source;
18
19use crate::tfhe::blind_rotation::{
20 BlincRotationExecute, BlindRotationKey, BlindRotationKeyAlloc, BlindRotationKeyEncryptSk, BlindRotationKeyPrepared, CGGI,
21 LookUpTable, cggi_blind_rotate_scratch_space, mod_switch_2n,
22};
23
24use core::layouts::{
25 GLWECiphertext, GLWEPlaintext, GLWESecret, Infos, LWECiphertext, LWECiphertextToRef, LWEPlaintext, LWESecret,
26 prepared::{GLWESecretPrepared, PrepareAlloc},
27};
28
29pub fn test_blind_rotation<B: Backend>(module: &Module<B>, n_lwe: usize, block_size: usize, extension_factor: usize)
30where
31 Module<B>: VecZnxBigAllocBytes
32 + VecZnxDftAllocBytes
33 + SvpPPolAllocBytes
34 + VmpApplyTmpBytes
35 + VecZnxBigNormalizeTmpBytes
36 + VecZnxDftToVecZnxBigTmpBytes
37 + VecZnxDftToVecZnxBig<B>
38 + VecZnxDftAdd<B>
39 + VecZnxDftAddInplace<B>
40 + VecZnxDftFromVecZnx<B>
41 + VecZnxDftZero<B>
42 + SvpApply<B>
43 + VecZnxDftSubABInplace<B>
44 + VecZnxBigAddSmallInplace<B>
45 + VecZnxRotate
46 + VecZnxAddInplace
47 + VecZnxSubABInplace
48 + VecZnxNormalize<B>
49 + VecZnxNormalizeInplace<B>
50 + VecZnxCopy
51 + VecZnxMulXpMinusOneInplace
52 + SvpPrepare<B>
53 + SvpPPolAlloc<B>
54 + SvpApplyInplace<B>
55 + VecZnxDftToVecZnxBigConsume<B>
56 + VecZnxBigAddInplace<B>
57 + VecZnxBigNormalize<B>
58 + VecZnxNormalizeTmpBytes
59 + VecZnxFillUniform
60 + VecZnxAddNormal
61 + VecZnxAddScalarInplace
62 + VecZnxRotateInplace
63 + VecZnxSwithcDegree
64 + VecZnxSub
65 + VmpPMatAlloc<B>
66 + VmpPrepare<B>
67 + VmpApply<B>
68 + VmpApplyAdd<B>,
69 B: VecZnxDftAllocBytesImpl<B>
70 + VecZnxBigAllocBytesImpl<B>
71 + ScratchOwnedAllocImpl<B>
72 + ScratchOwnedBorrowImpl<B>
73 + TakeVecZnxDftImpl<B>
74 + TakeVecZnxBigImpl<B>
75 + TakeVecZnxDftSliceImpl<B>
76 + ScratchAvailableImpl<B>
77 + TakeVecZnxImpl<B>
78 + TakeVecZnxSliceImpl<B>,
79{
80 let n: usize = module.n();
81 let basek: usize = 19;
82 let k_lwe: usize = 24;
83 let k_brk: usize = 3 * basek;
84 let rows_brk: usize = 2; // Ensures first limb is noise-free.
85 let k_lut: usize = 1 * basek;
86 let k_res: usize = 2 * basek;
87 let rank: usize = 1;
88
89 let log_message_modulus = 4;
90
91 let message_modulus: usize = 1 << log_message_modulus;
92
93 let mut source_xs: Source = Source::new([2u8; 32]);
94 let mut source_xe: Source = Source::new([2u8; 32]);
95 let mut source_xa: Source = Source::new([1u8; 32]);
96
97 let mut scratch: ScratchOwned<B> = ScratchOwned::<B>::alloc(BlindRotationKey::generate_from_sk_scratch_space(
98 module, n, basek, k_brk, rank,
99 ));
100
101 let mut sk_glwe: GLWESecret<Vec<u8>> = GLWESecret::alloc(n, rank);
102 sk_glwe.fill_ternary_prob(0.5, &mut source_xs);
103 let sk_glwe_dft: GLWESecretPrepared<Vec<u8>, B> = sk_glwe.prepare_alloc(module, scratch.borrow());
104
105 let mut sk_lwe: LWESecret<Vec<u8>> = LWESecret::alloc(n_lwe);
106 sk_lwe.fill_binary_block(block_size, &mut source_xs);
107
108 let mut scratch_br: ScratchOwned<B> = ScratchOwned::<B>::alloc(cggi_blind_rotate_scratch_space(
109 module,
110 n,
111 block_size,
112 extension_factor,
113 basek,
114 k_res,
115 k_brk,
116 rows_brk,
117 rank,
118 ));
119
120 let mut brk: BlindRotationKey<Vec<u8>, CGGI> =
121 BlindRotationKey::<Vec<u8>, CGGI>::alloc(n, n_lwe, basek, k_brk, rows_brk, rank);
122
123 brk.encrypt_sk(
124 module,
125 &sk_glwe_dft,
126 &sk_lwe,
127 &mut source_xa,
128 &mut source_xe,
129 3.2,
130 scratch.borrow(),
131 );
132
133 let mut lwe: LWECiphertext<Vec<u8>> = LWECiphertext::alloc(n_lwe, basek, k_lwe);
134
135 let mut pt_lwe: LWEPlaintext<Vec<u8>> = LWEPlaintext::alloc(basek, k_lwe);
136
137 let x: i64 = 15 % (message_modulus as i64);
138
139 pt_lwe.encode_i64(x, log_message_modulus + 1);
140
141 lwe.encrypt_sk(
142 module,
143 &pt_lwe,
144 &sk_lwe,
145 &mut source_xa,
146 &mut source_xe,
147 3.2,
148 );
149
150 let f = |x: i64| -> i64 { 2 * x + 1 };
151
152 let mut f_vec: Vec<i64> = vec![0i64; message_modulus];
153 f_vec
154 .iter_mut()
155 .enumerate()
156 .for_each(|(i, x)| *x = f(i as i64));
157
158 let mut lut: LookUpTable = LookUpTable::alloc(n, basek, k_lut, extension_factor);
159 lut.set(module, &f_vec, log_message_modulus + 1);
160
161 let mut res: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(n, basek, k_res, rank);
162
163 let brk_prepared: BlindRotationKeyPrepared<Vec<u8>, CGGI, B> = brk.prepare_alloc(module, scratch.borrow());
164
165 brk_prepared.execute(module, &mut res, &lwe, &lut, scratch_br.borrow());
166
167 let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(n, basek, k_res);
168
169 res.decrypt(module, &mut pt_have, &sk_glwe_dft, scratch.borrow());
170
171 let mut lwe_2n: Vec<i64> = vec![0i64; lwe.n() + 1]; // TODO: from scratch space
172
173 mod_switch_2n(
174 2 * lut.domain_size(),
175 &mut lwe_2n,
176 &lwe.to_ref(),
177 lut.rotation_direction(),
178 );
179
180 let pt_want: i64 = (lwe_2n[0]
181 + lwe_2n[1..]
182 .iter()
183 .zip(sk_lwe.raw())
184 .map(|(x, y)| x * y)
185 .sum::<i64>())
186 & (2 * lut.domain_size() - 1) as i64;
187
188 lut.rotate(module, pt_want);
189
190 // First limb should be exactly equal (test are parameterized such that the noise does not reach
191 // the first limb)
192 assert_eq!(pt_have.data.at(0, 0), lut.data[0].at(0, 0));
193
194 // Verify that it effectively compute f(x)
195 let mut have: i64 = pt_have.decode_coeff_i64(log_message_modulus + 1, 0);
196
197 // Get positive representative and assert equality
198 have = (have + message_modulus as i64) % (message_modulus as i64);
199
200 assert_eq!(have, f(x) % (message_modulus as i64));
201}