From a8e8743b5038a3995e15aaefcca6b5c51fd9b222 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Bossuat Date: Tue, 15 Jul 2025 20:09:37 +0200 Subject: [PATCH] Various fixes --- core/src/blind_rotation/cggi.rs | 5 +-- core/src/glwe/ops.rs | 40 ++++++++++-------- core/src/glwe/packing.rs | 2 +- core/src/glwe/test_fft64/encryption.rs | 57 ++++++++------------------ 4 files changed, 43 insertions(+), 61 deletions(-) diff --git a/core/src/blind_rotation/cggi.rs b/core/src/blind_rotation/cggi.rs index 2cf1d88..d9ada60 100644 --- a/core/src/blind_rotation/cggi.rs +++ b/core/src/blind_rotation/cggi.rs @@ -1,7 +1,6 @@ use backend::{ - FFT64, MatZnxDftOps, MatZnxDftScratch, Module, ScalarZnxDft, ScalarZnxDftAlloc, ScalarZnxDftOps, - Scratch, VecZnxAlloc, VecZnxBigAlloc, VecZnxBigOps, VecZnxBigScratch, VecZnxDftAlloc, VecZnxDftOps, VecZnxOps, ZnxView, - ZnxViewMut, ZnxZero, + FFT64, MatZnxDftOps, MatZnxDftScratch, Module, ScalarZnxDft, ScalarZnxDftAlloc, ScalarZnxDftOps, Scratch, VecZnxAlloc, + VecZnxBigAlloc, VecZnxBigOps, VecZnxBigScratch, VecZnxDftAlloc, VecZnxDftOps, VecZnxOps, ZnxView, ZnxViewMut, ZnxZero, }; use itertools::izip; diff --git a/core/src/glwe/ops.rs b/core/src/glwe/ops.rs index 46f6bdc..48034a5 100644 --- a/core/src/glwe/ops.rs +++ b/core/src/glwe/ops.rs @@ -47,7 +47,7 @@ pub trait GLWEOps: GLWECiphertextToMut + SetMetaData + Sized { }); self.set_basek(a.basek()); - self.set_k(set_k(self, a, b)); + self.set_k(set_k_binary(self, a, b)); } fn add_inplace(&mut self, module: &Module, a: &A) @@ -69,9 +69,7 @@ pub trait GLWEOps: GLWECiphertextToMut + SetMetaData + Sized { module.vec_znx_add_inplace(&mut self_mut.data, i, &a_ref.data, i); }); - if a.rank() != 0 { - self.set_k(a.k().min(self.k())); - } + self.set_k(set_k_unary(self, a)) } fn sub(&mut self, module: &Module, a: &A, b: &B) @@ -119,7 +117,7 @@ pub trait GLWEOps: GLWECiphertextToMut + SetMetaData + Sized { }); self.set_basek(a.basek()); - self.set_k(set_k(self, a, b)); + self.set_k(set_k_binary(self, a, b)); } fn sub_inplace_ab(&mut self, module: &Module, a: &A) @@ -141,9 +139,7 @@ pub trait GLWEOps: GLWECiphertextToMut + SetMetaData + Sized { module.vec_znx_sub_ab_inplace(&mut self_mut.data, i, &a_ref.data, i); }); - if a.rank() != 0 { - self.set_k(a.k().min(self.k())); - } + self.set_k(set_k_unary(self, a)) } fn sub_inplace_ba(&mut self, module: &Module, a: &A) @@ -165,9 +161,7 @@ pub trait GLWEOps: GLWECiphertextToMut + SetMetaData + Sized { module.vec_znx_sub_ba_inplace(&mut self_mut.data, i, &a_ref.data, i); }); - if a.rank() != 0 { - self.set_k(a.k().min(self.k())); - } + self.set_k(set_k_unary(self, a)) } fn rotate(&mut self, module: &Module, k: i64, a: &A) @@ -189,9 +183,7 @@ pub trait GLWEOps: GLWECiphertextToMut + SetMetaData + Sized { }); self.set_basek(a.basek()); - if a.rank() != 0 { - self.set_k(a.k().min(self.k())); - } + self.set_k(set_k_unary(self, a)) } fn rotate_inplace(&mut self, module: &Module, k: i64) { @@ -216,8 +208,6 @@ pub trait GLWEOps: GLWECiphertextToMut + SetMetaData + Sized { assert_eq!(self.n(), module.n()); assert_eq!(a.n(), module.n()); assert_eq!(self.rank(), a.rank()); - assert_eq!(self.k(), a.k()); - assert_eq!(self.basek(), a.basek()); } let self_mut: &mut GLWECiphertext<&mut [u8]> = &mut self.to_mut(); @@ -227,7 +217,7 @@ pub trait GLWEOps: GLWECiphertextToMut + SetMetaData + Sized { module.vec_znx_copy(&mut self_mut.data, i, &a_ref.data, i); }); - self.set_k(a.k()); + self.set_k(a.k().min(self.size() * self.basek())); self.set_basek(a.basek()); } @@ -277,17 +267,31 @@ impl GLWECiphertext> { } // c = op(a, b) -fn set_k(c: &impl Infos, a: &impl Infos, b: &impl Infos) -> usize { +fn set_k_binary(c: &impl Infos, a: &impl Infos, b: &impl Infos) -> usize { + // If either operands is a ciphertext if a.rank() != 0 || b.rank() != 0 { + // If a is a plaintext (but b ciphertext) let k = if a.rank() == 0 { b.k() + // If b is a plaintext (but a ciphertext) } else if b.rank() == 0 { a.k() + // If a & b are both ciphertexts } else { a.k().min(b.k()) }; k.min(c.k()) + // If a & b are both plaintexts } else { c.k() } } + +// a = op(a, b) +fn set_k_unary(a: &impl Infos, b: &impl Infos) -> usize { + if a.rank() != 0 || b.rank() != 0 { + a.k().min(b.k()) + } else { + a.k() + } +} diff --git a/core/src/glwe/packing.rs b/core/src/glwe/packing.rs index bfd7f36..f38504b 100644 --- a/core/src/glwe/packing.rs +++ b/core/src/glwe/packing.rs @@ -1,7 +1,7 @@ use crate::{GLWEAutomorphismKey, GLWECiphertext, GLWEOps, Infos, ScratchCore}; use std::collections::HashMap; -use backend::{FFT64, Module, Scratch, VecZnxOps}; +use backend::{FFT64, Module, Scratch}; /// [StreamPacker] enables only the fly GLWE packing /// with constant memory of Log(N) ciphertexts. diff --git a/core/src/glwe/test_fft64/encryption.rs b/core/src/glwe/test_fft64/encryption.rs index 2d82575..55cd5a9 100644 --- a/core/src/glwe/test_fft64/encryption.rs +++ b/core/src/glwe/test_fft64/encryption.rs @@ -1,8 +1,7 @@ -use backend::{Decoding, Encoding, FFT64, Module, ScratchOwned, Stats, VecZnxOps, ZnxZero}; -use itertools::izip; +use backend::{FFT64, FillUniform, Module, ScratchOwned, Stats}; use sampling::source::Source; -use crate::{FourierGLWECiphertext, FourierGLWESecret, GLWECiphertext, GLWEPlaintext, GLWEPublicKey, GLWESecret, Infos}; +use crate::{FourierGLWECiphertext, FourierGLWESecret, GLWECiphertext, GLWEOps, GLWEPlaintext, GLWEPublicKey, GLWESecret, Infos}; #[test] fn encrypt_sk() { @@ -35,7 +34,8 @@ fn test_encrypt_sk(log_n: usize, basek: usize, k_ct: usize, k_pt: usize, sigma: let module: Module = Module::::new(1 << log_n); let mut ct: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_ct, rank); - let mut pt: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_pt); + let mut pt_want: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_pt); + let mut pt_have: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_pt); let mut source_xs: Source = Source::new([0u8; 32]); let mut source_xe: Source = Source::new([0u8; 32]); @@ -50,17 +50,13 @@ fn test_encrypt_sk(log_n: usize, basek: usize, k_ct: usize, k_pt: usize, sigma: sk.fill_ternary_prob(0.5, &mut source_xs); let sk_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk); - let mut data_want: Vec = vec![0i64; module.n()]; - - data_want - .iter_mut() - .for_each(|x| *x = source_xa.next_i64() & 0xFF); - - pt.data.encode_vec_i64(0, basek, k_pt, &data_want, 10); + pt_want + .data + .fill_uniform(basek, 0, pt_want.size(), &mut source_xa); ct.encrypt_sk( &module, - &pt, + &pt_want, &sk_dft, &mut source_xa, &mut source_xe, @@ -68,26 +64,14 @@ fn test_encrypt_sk(log_n: usize, basek: usize, k_ct: usize, k_pt: usize, sigma: scratch.borrow(), ); - pt.data.zero(); + ct.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow()); - ct.decrypt(&module, &mut pt, &sk_dft, scratch.borrow()); + pt_want.sub_inplace_ab(&module, &pt_have); - let mut data_have: Vec = vec![0i64; module.n()]; + let noise_have: f64 = pt_want.data.std(0, basek) * (ct.k() as f64).exp2(); + let noise_want: f64 = sigma; - pt.data - .decode_vec_i64(0, basek, pt.size() * basek, &mut data_have); - - // TODO: properly assert the decryption noise through std(dec(ct) - pt) - let scale: f64 = (1 << (pt.size() * basek - k_pt)) as f64; - izip!(data_want.iter(), data_have.iter()).for_each(|(a, b)| { - let b_scaled = (*b as f64) / scale; - assert!( - (*a as f64 - b_scaled).abs() < 0.1, - "{} {}", - *a as f64, - b_scaled - ) - }); + assert!(noise_have <= noise_want + 0.2); } fn test_encrypt_zero_sk(log_n: usize, basek: usize, k_ct: usize, sigma: f64, rank: usize) { @@ -127,6 +111,7 @@ fn test_encrypt_pk(log_n: usize, basek: usize, k_ct: usize, k_pk: usize, sigma: let module: Module = Module::::new(1 << log_n); let mut ct: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_ct, rank); + let mut pt_have = GLWEPlaintext::alloc(&module, basek, k_ct); let mut pt_want: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_ct); let mut source_xs: Source = Source::new([0u8; 32]); @@ -147,13 +132,9 @@ fn test_encrypt_pk(log_n: usize, basek: usize, k_ct: usize, k_pk: usize, sigma: | GLWECiphertext::encrypt_pk_scratch_space(&module, basek, pk.k()), ); - let mut data_want: Vec = vec![0i64; module.n()]; - - data_want - .iter_mut() - .for_each(|x| *x = source_xa.next_i64() & 0); - - pt_want.data.encode_vec_i64(0, basek, k_ct, &data_want, 10); + pt_want + .data + .fill_uniform(basek, 0, pt_want.size(), &mut source_xa); ct.encrypt_pk( &module, @@ -165,11 +146,9 @@ fn test_encrypt_pk(log_n: usize, basek: usize, k_ct: usize, k_pk: usize, sigma: scratch.borrow(), ); - let mut pt_have: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_ct); - ct.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow()); - module.vec_znx_sub_ab_inplace(&mut pt_want.data, 0, &pt_have.data, 0); + pt_want.sub_inplace_ab(&module, &pt_have); let noise_have: f64 = pt_want.data.std(0, basek).log2(); let noise_want: f64 = ((((rank as f64) + 1.0) * module.n() as f64 * 0.5 * sigma * sigma).sqrt()).log2() - (k_ct as f64);