From f3a368ab6ac173535c76199985b87a0eb1febfc2 Mon Sep 17 00:00:00 2001 From: arnaucube Date: Sun, 22 Jun 2025 13:24:35 +0200 Subject: [PATCH] add ciphertext-by-const (plaintext) addition & mult --- arithmetic/src/ring.rs | 5 ++ bfv/src/lib.rs | 106 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 107 insertions(+), 4 deletions(-) diff --git a/arithmetic/src/ring.rs b/arithmetic/src/ring.rs index 2bdf3be..512234c 100644 --- a/arithmetic/src/ring.rs +++ b/arithmetic/src/ring.rs @@ -85,6 +85,11 @@ impl PR { evals: None, }) } + // Warning: this method assumes Q < P + pub fn remodule(&self) -> PR { + assert!(Q < P); + PR::::from_vec_u64(self.coeffs().iter().map(|m_i| m_i.0).collect()) + } // TODO review if needed, or if with this interface pub fn mul_by_matrix(&self, m: &Vec>>) -> Result>> { diff --git a/bfv/src/lib.rs b/bfv/src/lib.rs index f8ce811..3bb1d19 100644 --- a/bfv/src/lib.rs +++ b/bfv/src/lib.rs @@ -10,7 +10,7 @@ use rand::Rng; use rand_distr::{Normal, Uniform}; use std::ops; -use arithmetic::PR; +use arithmetic::{Zq, PR}; // error deviation for the Gaussian(Normal) distribution // sigma=3.2 from: https://eprint.iacr.org/2022/162.pdf page 5 @@ -30,9 +30,6 @@ impl RLWE { fn add(lhs: Self, rhs: Self) -> Self { RLWE::(lhs.0 + rhs.0, lhs.1 + rhs.1) } - fn mul(lhs: Self, rhs: Self) -> Self { - todo!() - } } impl ops::Add> for RLWE { @@ -42,6 +39,21 @@ impl ops::Add> for RLWE { } } +impl ops::Add<&PR> for &RLWE { + type Output = RLWE; + fn add(self, rhs: &PR) -> Self::Output { + // todo!() + BFV::::add_const(self, rhs) + } +} +impl ops::Mul<&PR> for &RLWE { + type Output = RLWE; + fn mul(self, rhs: &PR) -> Self::Output { + // todo!() + BFV::::mul_const(&self, rhs) + } +} + pub struct BFV {} impl BFV { @@ -92,6 +104,17 @@ impl BFV { .collect(); PR::::from_vec_u64(r) } + + fn add_const(c: &RLWE, m: &PR) -> RLWE { + // assuming T to Zq + let m = m.remodule::(); + RLWE::(c.0 + m * Self::DELTA, c.1) + } + fn mul_const(c: &RLWE, m: &PR) -> RLWE { + // assuming T to Zq + let m = m.remodule::(); + RLWE::(c.0 * m * Self::DELTA, c.1) + } } #[cfg(test)] @@ -149,4 +172,79 @@ mod tests { Ok(()) } + + #[test] + fn test_constant_add_mul() -> Result<()> { + const Q: u64 = 2u64.pow(16) + 1; + const N: usize = 32; + const T: u64 = 4; // plaintext modulus + type S = BFV; + + let mut rng = rand::thread_rng(); + + let (sk, pk) = S::new_key(&mut rng)?; + + let msg_dist = Uniform::new(0_u64, T); + let m1 = PR::::rand_u64(&mut rng, msg_dist)?; + let m2_const = PR::::rand_u64(&mut rng, msg_dist)?; + + let c1 = S::encrypt(&mut rng, &pk, &m1)?; + + let c3_add = &c1 + &m2_const; + let c3_mul = &c1 * &m2_const; + + let m3_add_recovered = S::decrypt(&sk, &c3_add); + let m3_mul_recovered = S::decrypt(&sk, &c3_mul); + + assert_eq!(m1 + m2_const, m3_add_recovered); + + let mut mul_res = naive_poly_mul::(&m1.coeffs().to_vec(), &m2_const.coeffs().to_vec()); + arithmetic::ring::modulus::(&mut mul_res); + dbg!(&mul_res); + let mul_res_2 = + naive_poly_mul_2::(&m1.coeffs().to_vec(), &m2_const.coeffs().to_vec()); + assert_eq!(mul_res, mul_res_2); + let mul_res = PR::::from_vec(mul_res); + assert_eq!(mul_res.coeffs(), m3_mul_recovered.coeffs()); + + Ok(()) + } + + fn naive_poly_mul(a: &[Zq], b: &[Zq]) -> Vec> { + let mut result: Vec> = vec![Zq::zero(); a.len() + b.len() - 1]; + for (i, &ai) in a.iter().enumerate() { + for (j, &bj) in b.iter().enumerate() { + result[i + j] = result[i + j] + (ai * bj); + } + } + result + } + fn naive_poly_mul_2( + poly1: &[Zq], + poly2: &[Zq], + ) -> Vec> { + let degree1 = poly1.len(); + let degree2 = poly2.len(); + + // The degree of the resulting polynomial will be degree1 + degree2 - 1 + let mut result = vec![Zq::zero(); degree1 + degree2 - 1]; + + // Perform the multiplication + for i in 0..degree1 { + for j in 0..degree2 { + result[i + j] = result[i + j] + poly1[i] * poly2[j]; + } + } + + // Reduce the result modulo x^N + 1 + let mut reduced_result = vec![Zq::zero(); N]; + + for i in 0..result.len() { + let mod_index = i % N; // wrap around using modulo N + reduced_result[mod_index] += result[i]; + } + + // Return the reduced polynomial + reduced_result + } }