arnaucube
arnaucube/poulpyPublic Self-hosted repository
Fix seeded glwe encryption
Pro7ech jeanphilippe.bossuat@gmail.com
Tue, 18 Nov 2025 23:57:24 +0100
commit

59a1b6616ae2aa1afecd7bd2e177c2b1983468c1

parent

2fb16276135cafa2c5ba0383a72c1bf23ef19667

M poulpy-core/src/encryption/compressed/gglwe.rs → poulpy-core/src/encryption/compressed/gglwe.rs

@@ -143,20 +143,21 @@

let mut source_xa = Source::new(seed); let (mut tmp_pt, scrach_1) = scratch.take_glwe_plaintext(res); - for col_i in 0..rank_in { - for d_i in 0..dnum { + + for col_j in 0..rank_in { + for row_i in 0..dnum { // Adds the scalar_znx_pt to the i-th limb of the vec_znx_pt tmp_pt.data.zero(); // zeroes for next iteration - self.vec_znx_add_scalar_inplace(&mut tmp_pt.data, 0, (dsize - 1) + d_i * dsize, pt, col_i); + self.vec_znx_add_scalar_inplace(&mut tmp_pt.data, 0, (dsize - 1) + row_i * dsize, pt, col_j); self.vec_znx_normalize_inplace(base2k, &mut tmp_pt.data, 0, scrach_1); let (seed, mut source_xa_tmp) = source_xa.branch(); - seeds[col_i * dnum + d_i] = seed; + seeds[row_i * rank_in + col_j] = seed; self.glwe_encrypt_sk_internal( res.base2k().into(), res.k().into(), - &mut res.at_mut(d_i, col_i).data, + &mut res.at_mut(row_i, col_j).data, cols, true, Some((&tmp_pt, 0)),
M poulpy-core/src/layouts/compressed/gglwe.rs → poulpy-core/src/layouts/compressed/gglwe.rs

@@ -263,9 +263,8 @@ assert!(res.dnum() <= other.dnum());

let rank_in: usize = res.rank_in().into(); let dnum: usize = res.dnum().into(); - - for row_i in 0..dnum { - for col_i in 0..rank_in { + for col_i in 0..rank_in { + for row_i in 0..dnum { self.decompress_glwe(&mut res.at_mut(row_i, col_i), &other.at(row_i, col_i)); } }
M poulpy-core/src/noise/gglwe.rs → poulpy-core/src/noise/gglwe.rs

@@ -1,6 +1,6 @@

use poulpy_hal::{ api::VecZnxAddScalarInplace, - layouts::{Backend, DataRef, Module, ScalarZnxToRef, Scratch, Stats, ZnxInfos, ZnxZero}, + layouts::{Backend, DataRef, Module, ScalarZnxToRef, Scratch, Stats, ZnxZero}, }; use crate::{

@@ -78,7 +78,6 @@ let res: &GGLWE<&[u8]> = &res.to_ref();

let dsize: usize = res.dsize().into(); let (mut pt, scratch_1) = scratch.take_glwe_plaintext(res); pt.data_mut().zero(); - println!("col: {res_col} {}", pt_want.to_ref().cols()); self.vec_znx_add_scalar_inplace( &mut pt.data, 0,
M poulpy-core/src/tests/mod.rs → poulpy-core/src/tests/mod.rs

@@ -35,6 +35,7 @@ glwe_packer => crate::tests::test_suite::test_glwe_packer,

// GGLWE Encryption gglwe_switching_key_encrypt_sk => crate::tests::test_suite::encryption::test_gglwe_switching_key_encrypt_sk, gglwe_switching_key_compressed_encrypt_sk => crate::tests::test_suite::encryption::test_gglwe_switching_key_compressed_encrypt_sk, +gglwe_compressed_encrypt_sk => crate::tests::test_suite::encryption::test_gglwe_compressed_encrypt_sk, gglwe_automorphism_key_encrypt_sk => crate::tests::test_suite::encryption::test_gglwe_automorphism_key_encrypt_sk, gglwe_automorphism_key_compressed_encrypt_sk => crate::tests::test_suite::encryption::test_gglwe_automorphism_key_compressed_encrypt_sk, gglwe_tensor_key_encrypt_sk => crate::tests::test_suite::encryption::test_gglwe_tensor_key_encrypt_sk,

@@ -98,6 +99,7 @@ glwe_packer => crate::tests::test_suite::test_glwe_packer,

// GGLWE Encryption gglwe_switching_key_encrypt_sk => crate::tests::test_suite::encryption::test_gglwe_switching_key_encrypt_sk, gglwe_switching_key_compressed_encrypt_sk => crate::tests::test_suite::encryption::test_gglwe_switching_key_compressed_encrypt_sk, +gglwe_compressed_encrypt_sk => crate::tests::test_suite::encryption::test_gglwe_compressed_encrypt_sk, gglwe_automorphism_key_encrypt_sk => crate::tests::test_suite::encryption::test_gglwe_automorphism_key_encrypt_sk, gglwe_automorphism_key_compressed_encrypt_sk => crate::tests::test_suite::encryption::test_gglwe_automorphism_key_compressed_encrypt_sk, gglwe_tensor_key_encrypt_sk => crate::tests::test_suite::encryption::test_gglwe_tensor_key_encrypt_sk,
M poulpy-core/src/tests/test_suite/conversion.rs → poulpy-core/src/tests/test_suite/conversion.rs

@@ -275,8 +275,6 @@ data[a_idx] = 17;

let mut glwe_pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&glwe_infos); glwe_pt.encode_vec_i64(&data, k_lwe_pt); - println!("glwe_pt: {glwe_pt}"); - let mut glwe_ct: GLWE<Vec<u8>> = GLWE::alloc_from_infos(&glwe_infos); glwe_ct.encrypt_sk( module,
M poulpy-core/src/tests/test_suite/encryption/gglwe_atk.rs → poulpy-core/src/tests/test_suite/encryption/gglwe_atk.rs

@@ -176,7 +176,6 @@ atk.decompress(module, &atk_compressed);

let max_noise: f64 = SIGMA.log2() - (atk.k().as_usize() as f64) + 0.5; - println!("rank: {rank} dsize: {dsize} dnum: {dnum}"); for row in 0..atk.dnum().as_usize() { for col in 0..atk.rank().as_usize() { let noise_have = atk
M poulpy-core/src/tests/test_suite/encryption/gglwe_ct.rs → poulpy-core/src/tests/test_suite/encryption/gglwe_ct.rs

@@ -5,12 +5,13 @@ source::Source,

}; use crate::{ - GGLWEEncryptSk, GGLWEKeyswitch, GLWESwitchingKeyCompressedEncryptSk, GLWESwitchingKeyEncryptSk, ScratchTakeCore, + GGLWECompressedEncryptSk, GGLWEEncryptSk, GGLWEKeyswitch, GLWESwitchingKeyCompressedEncryptSk, GLWESwitchingKeyEncryptSk, + ScratchTakeCore, decryption::GLWEDecrypt, encryption::SIGMA, layouts::{ - GGLWEInfos, GGLWELayout, GLWESecret, GLWESecretPreparedFactory, GLWESwitchingKey, GLWESwitchingKeyCompressed, - GLWESwitchingKeyDecompress, LWEInfos, + GGLWE, GGLWECompressed, GGLWEInfos, GGLWELayout, GLWESecret, GLWESecretPreparedFactory, + GLWESwitchingKey, GLWESwitchingKeyCompressed, GLWESwitchingKeyDecompress, LWEInfos, prepared::{GGLWEPreparedFactory, GLWESecretPrepared}, }, noise::GGLWENoise,

@@ -121,18 +122,18 @@ {

let n: usize = module.n(); let base2k: usize = 12; let k_ksk: usize = 54; - let dsize: usize = k_ksk / base2k; + let max_dsize: usize = k_ksk / base2k; for rank_in in 1_usize..3 { for rank_out in 1_usize..3 { - for di in 1_usize..dsize + 1 { - let dnum: usize = (k_ksk - di * base2k) / (di * base2k); + for dsize in 1_usize..max_dsize { + let dnum: usize = (k_ksk - dsize * base2k) / (dsize * base2k); let gglwe_infos: GGLWELayout = GGLWELayout { n: n.into(), base2k: base2k.into(), k: k_ksk.into(), dnum: dnum.into(), - dsize: di.into(), + dsize: dsize.into(), rank_in: rank_in.into(), rank_out: rank_out.into(), };

@@ -198,3 +199,95 @@ }

} } } + +pub fn test_gglwe_compressed_encrypt_sk<BE: Backend>(module: &Module<BE>) +where + Module<BE>: GGLWEEncryptSk<BE> + + GGLWEPreparedFactory<BE> + + GGLWEKeyswitch<BE> + + GLWEDecrypt<BE> + + GLWESecretPreparedFactory<BE> + + GLWESwitchingKeyEncryptSk<BE> + + GGLWECompressedEncryptSk<BE> + + GLWESwitchingKeyDecompress + + GGLWENoise<BE> + + VecZnxFillUniform, + ScratchOwned<BE>: ScratchOwnedAlloc<BE> + ScratchOwnedBorrow<BE>, + Scratch<BE>: ScratchAvailable + ScratchTakeCore<BE>, +{ + let n: usize = module.n(); + let base2k: usize = 12; + let k_ksk: usize = 54; + let max_dsize: usize = k_ksk / base2k; + for rank_in in 1_usize..3 { + for rank_out in 1_usize..3 { + for dsize in 1_usize..max_dsize + 1 { + let dnum: usize = (k_ksk - dsize * base2k) / (dsize * base2k); + + let gglwe_infos: GGLWELayout = GGLWELayout { + n: n.into(), + base2k: base2k.into(), + k: k_ksk.into(), + dnum: dnum.into(), + dsize: dsize.into(), + rank_in: rank_in.into(), + rank_out: rank_out.into(), + }; + + let mut ksk_compressed: GGLWECompressed<Vec<u8>> = GGLWECompressed::alloc_from_infos(&gglwe_infos); + + let mut source_xs: Source = Source::new([0u8; 32]); + let mut source_xe: Source = Source::new([0u8; 32]); + + let mut scratch: ScratchOwned<BE> = + ScratchOwned::alloc(GGLWECompressed::encrypt_sk_tmp_bytes(module, &gglwe_infos)); + + let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(n.into(), rank_in.into()); + sk_in.fill_ternary_prob(0.5, &mut source_xs); + + let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(n.into(), rank_out.into()); + sk_out.fill_ternary_prob(0.5, &mut source_xs); + let mut sk_out_prepared: GLWESecretPrepared<Vec<u8>, BE> = GLWESecretPrepared::alloc(module, rank_out.into()); + sk_out_prepared.prepare(module, &sk_out); + + let seed_xa = [1u8; 32]; + + ksk_compressed.encrypt_sk( + module, + &sk_in.data, + &sk_out_prepared, + seed_xa, + &mut source_xe, + scratch.borrow(), + ); + + let mut ksk: GGLWE<Vec<u8>> = GGLWE::alloc_from_infos(&gglwe_infos); + ksk.decompress(module, &ksk_compressed); + + let max_noise: f64 = SIGMA.log2() - (ksk.k().as_usize() as f64) + 0.5; + + for row in 0..ksk.dnum().as_usize() { + for col in 0..ksk.rank_in().as_usize() { + let noise_have = ksk + .noise( + module, + row, + col, + &sk_in.data, + &sk_out_prepared, + scratch.borrow(), + ) + .std() + .log2(); + + assert!( + noise_have < max_noise + 0.5, + "row:{row} col:{col} noise_have:{noise_have} > max_noise:{}", + max_noise + 0.5 + ); + } + } + } + } + } +}
M poulpy-core/src/tests/test_suite/encryption/gglwe_to_ggsw_key.rs → poulpy-core/src/tests/test_suite/encryption/gglwe_to_ggsw_key.rs

@@ -10,8 +10,8 @@ decryption::GLWEDecrypt,

encryption::SIGMA, layouts::{ Dsize, GGLWE, GGLWEDecompress, GGLWEInfos, GGLWEToGGSWKey, GGLWEToGGSWKeyCompressed, GGLWEToGGSWKeyDecompress, - GGLWEToGGSWKeyLayout, GLWESecret, GLWESecretPreparedFactory, GLWESecretTensor, GLWESecretTensorFactory, - LWEInfos, prepared::GLWESecretPrepared, + GGLWEToGGSWKeyLayout, GLWESecret, GLWESecretPreparedFactory, GLWESecretTensor, GLWESecretTensorFactory, LWEInfos, + prepared::GLWESecretPrepared, }, };
M poulpy-core/src/tests/test_suite/encryption/glwe_tsk.rs → poulpy-core/src/tests/test_suite/encryption/glwe_tsk.rs

@@ -9,7 +9,7 @@ GGLWENoise, GLWETensorKeyCompressedEncryptSk, GLWETensorKeyEncryptSk, ScratchTakeCore,

decryption::GLWEDecrypt, encryption::SIGMA, layouts::{ - Dsize, GGLWEDecompress, GGLWEInfos, GLWEInfos, GLWESecret, GLWESecretPreparedFactory, GLWESecretTensor, + Dsize, GGLWEDecompress, GGLWEInfos, GLWESecret, GLWESecretPreparedFactory, GLWESecretTensor, GLWESecretTensorFactory, GLWETensorKey, GLWETensorKeyCompressed, GLWETensorKeyLayout, LWEInfos, prepared::GLWESecretPrepared, },

@@ -71,7 +71,7 @@

let max_noise: f64 = SIGMA.log2() - (tensor_key.k().as_usize() as f64) + 0.5; for row in 0..tensor_key.dnum().as_usize() { - for col in 0..tensor_key.rank().as_usize() + 1 { + for col in 0..tensor_key.rank_in().as_usize() { assert!( tensor_key .0

@@ -148,7 +148,7 @@

let max_noise: f64 = SIGMA.log2() - (tensor_key.k().as_usize() as f64) + 0.5; for row in 0..tensor_key.dnum().as_usize() { - for col in 0..tensor_key.rank().as_usize() + 1 { + for col in 0..tensor_key.rank_in().as_usize() { assert!( tensor_key .0
M poulpy-core/src/tests/test_suite/external_product/gglwe_ksk.rs → poulpy-core/src/tests/test_suite/external_product/gglwe_ksk.rs

@@ -8,7 +8,7 @@ use crate::{

GGLWEExternalProduct, GGLWENoise, GGSWEncryptSk, GLWESwitchingKeyEncryptSk, ScratchTakeCore, encryption::SIGMA, layouts::{ - GGLWEInfos, GGSW, GGSWLayout, GGSWPreparedFactory, GLWEInfos, GLWESecret, GLWESecretPreparedFactory, GLWESwitchingKey, + GGLWEInfos, GGSW, GGSWLayout, GGSWPreparedFactory, GLWESecret, GLWESecretPreparedFactory, GLWESwitchingKey, GLWESwitchingKeyLayout, prepared::{GGSWPrepared, GLWESecretPrepared}, },
M poulpy-schemes/src/tfhe/bdd_arithmetic/bdd_2w_to_1w.rs → poulpy-schemes/src/tfhe/bdd_arithmetic/bdd_2w_to_1w.rs

@@ -98,27 +98,6 @@ T::BITS as usize * self.data.len()

} } -pub struct JoinedBits<A, B> { - pub lo: A, - pub hi: B, - pub split: usize, // 32 in your example -} - -impl<A, B, BE> GetGGSWBit<BE> for JoinedBits<A, B> -where - BE: Backend, - A: GetGGSWBit<BE>, - B: GetGGSWBit<BE>, -{ - fn get_bit(&self, bit: usize) -> GGSWPrepared<&[u8], BE> { - if bit < self.split { - self.lo.get_bit(bit) - } else { - self.hi.get_bit(bit - self.split) - } - } -} - #[macro_export] macro_rules! define_bdd_2w_to_1w_trait { ($(#[$meta:meta])* $vis:vis $trait_name:ident, $method_name:ident) => {