arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/poulpy-schemes/src/tfhe/blind_rotation/tests/generic_blind_rotation.rs
poulpy-schemes/src/tfhe/blind_rotation/tests/generic_blind_rotation.rsRaw
  1use poulpy_hal::{
  2    api::{ScratchOwnedAlloc, ScratchOwnedBorrow},
  3    layouts::{Backend, Scratch, ScratchOwned, ZnxView},
  4    source::Source,
  5};
  6
  7use crate::tfhe::blind_rotation::{
  8    BlindRotationAlgo, BlindRotationExecute, BlindRotationKey, BlindRotationKeyEncryptSk, BlindRotationKeyFactory,
  9    BlindRotationKeyLayout, BlindRotationKeyPrepared, BlindRotationKeyPreparedFactory, LookUpTableLayout, LookupTable,
 10    LookupTableFactory, mod_switch_2n,
 11};
 12
 13use poulpy_core::{
 14    GLWEDecrypt, LWEEncryptSk, ScratchTakeCore,
 15    layouts::{
 16        GLWE, GLWELayout, GLWEPlaintext, GLWESecret, GLWESecretPreparedFactory, LWE, LWEInfos, LWELayout, LWEPlaintext,
 17        LWESecret, LWEToRef, prepared::GLWESecretPrepared,
 18    },
 19};
 20
 21pub fn test_blind_rotation<BRA: BlindRotationAlgo, M, BE: Backend>(
 22    module: &M,
 23    n_lwe: usize,
 24    block_size: usize,
 25    extension_factor: usize,
 26) where
 27    M: BlindRotationKeyEncryptSk<BRA, BE>
 28        + BlindRotationKeyPreparedFactory<BRA, BE>
 29        + BlindRotationExecute<BRA, BE>
 30        + LookupTableFactory
 31        + GLWESecretPreparedFactory<BE>
 32        + GLWEDecrypt<BE>
 33        + LWEEncryptSk<BE>,
 34    BlindRotationKey<Vec<u8>, BRA>: BlindRotationKeyFactory<BRA>,
 35    ScratchOwned<BE>: ScratchOwnedAlloc<BE> + ScratchOwnedBorrow<BE>,
 36    Scratch<BE>: ScratchTakeCore<BE>,
 37{
 38    let n_glwe: usize = module.n();
 39    let base2k: usize = 19;
 40    let k_lwe: usize = 24;
 41    let k_brk: usize = 3 * base2k;
 42    let rows_brk: usize = 2; // Ensures first limb is noise-free.
 43    let k_lut: usize = base2k;
 44    let k_res: usize = 2 * base2k;
 45    let rank: usize = 1;
 46
 47    let log_message_modulus: usize = 4;
 48
 49    let message_modulus: usize = 1 << log_message_modulus;
 50
 51    let mut source_xs: Source = Source::new([2u8; 32]);
 52    let mut source_xe: Source = Source::new([2u8; 32]);
 53    let mut source_xa: Source = Source::new([1u8; 32]);
 54
 55    let brk_infos: BlindRotationKeyLayout = BlindRotationKeyLayout {
 56        n_glwe: n_glwe.into(),
 57        n_lwe: n_lwe.into(),
 58        base2k: base2k.into(),
 59        k: k_brk.into(),
 60        dnum: rows_brk.into(),
 61        rank: rank.into(),
 62    };
 63
 64    let glwe_infos: GLWELayout = GLWELayout {
 65        n: n_glwe.into(),
 66        base2k: base2k.into(),
 67        k: k_res.into(),
 68        rank: rank.into(),
 69    };
 70
 71    let lwe_infos: LWELayout = LWELayout {
 72        n: n_lwe.into(),
 73        k: k_lwe.into(),
 74        base2k: base2k.into(),
 75    };
 76
 77    let mut scratch: ScratchOwned<BE> = ScratchOwned::<BE>::alloc(BlindRotationKey::encrypt_sk_tmp_bytes(module, &brk_infos));
 78
 79    let mut sk_glwe: GLWESecret<Vec<u8>> = GLWESecret::alloc_from_infos(&glwe_infos);
 80    sk_glwe.fill_ternary_prob(0.5, &mut source_xs);
 81    let mut sk_glwe_dft: GLWESecretPrepared<Vec<u8>, BE> = GLWESecretPrepared::alloc_from_infos(module, &glwe_infos);
 82    sk_glwe_dft.prepare(module, &sk_glwe);
 83
 84    let mut sk_lwe: LWESecret<Vec<u8>> = LWESecret::alloc(n_lwe.into());
 85    sk_lwe.fill_binary_block(block_size, &mut source_xs);
 86
 87    let mut scratch_br: ScratchOwned<BE> = ScratchOwned::<BE>::alloc(BlindRotationKeyPrepared::execute_tmp_bytes(
 88        module,
 89        block_size,
 90        extension_factor,
 91        &glwe_infos,
 92        &brk_infos,
 93    ));
 94
 95    let mut brk: BlindRotationKey<Vec<u8>, BRA> = BlindRotationKey::<Vec<u8>, BRA>::alloc(&brk_infos);
 96
 97    brk.encrypt_sk(
 98        module,
 99        &sk_glwe_dft,
100        &sk_lwe,
101        &mut source_xa,
102        &mut source_xe,
103        scratch.borrow(),
104    );
105
106    let mut lwe: LWE<Vec<u8>> = LWE::alloc_from_infos(&lwe_infos);
107
108    let mut pt_lwe: LWEPlaintext<Vec<u8>> = LWEPlaintext::alloc_from_infos(&lwe_infos);
109
110    let x: i64 = 15 % (message_modulus as i64);
111
112    pt_lwe.encode_i64(x, (log_message_modulus + 1).into());
113
114    lwe.encrypt_sk(
115        module,
116        &pt_lwe,
117        &sk_lwe,
118        &mut source_xa,
119        &mut source_xe,
120        scratch.borrow(),
121    );
122
123    let f = |x: i64| -> i64 { 2 * x + 1 };
124
125    let mut f_vec: Vec<i64> = vec![0i64; message_modulus];
126    f_vec
127        .iter_mut()
128        .enumerate()
129        .for_each(|(i, x)| *x = f(i as i64));
130
131    let lut_infos = LookUpTableLayout {
132        n: module.n().into(),
133        extension_factor,
134        k: k_lut.into(),
135        base2k: base2k.into(),
136    };
137
138    let mut lut: LookupTable = LookupTable::alloc(&lut_infos);
139    lut.set(module, &f_vec, log_message_modulus + 1);
140
141    let mut res: GLWE<Vec<u8>> = GLWE::alloc_from_infos(&glwe_infos);
142
143    let mut brk_prepared: BlindRotationKeyPrepared<Vec<u8>, BRA, BE> = BlindRotationKeyPrepared::alloc(module, &brk);
144    brk_prepared.prepare(module, &brk, scratch_br.borrow());
145
146    brk_prepared.execute(module, &mut res, &lwe, &lut, scratch_br.borrow());
147
148    let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&glwe_infos);
149
150    res.decrypt(module, &mut pt_have, &sk_glwe_dft, scratch.borrow());
151
152    let mut lwe_2n: Vec<i64> = vec![0i64; (lwe.n() + 1).into()]; // TODO: from scratch space
153
154    mod_switch_2n(
155        2 * lut.domain_size(),
156        &mut lwe_2n,
157        &lwe.to_ref(),
158        lut.rotation_direction(),
159    );
160
161    let pt_want: i64 = (lwe_2n[0]
162        + lwe_2n[1..]
163            .iter()
164            .zip(sk_lwe.raw())
165            .map(|(x, y)| x * y)
166            .sum::<i64>())
167        & (2 * lut.domain_size() - 1) as i64;
168
169    lut.rotate(module, pt_want);
170
171    // First limb should be exactly equal (test are parameterized such that the noise does not reach
172    // the first limb)
173    assert_eq!(pt_have.data.at(0, 0), lut.data[0].at(0, 0));
174
175    // Verify that it effectively compute f(x)
176    let mut have: i64 = pt_have.decode_coeff_i64((log_message_modulus + 1).into(), 0);
177
178    // Get positive representative and assert equality
179    have = (have + message_modulus as i64) % (message_modulus as i64);
180
181    assert_eq!(have, f(x) % (message_modulus as i64));
182}