mirror of
https://github.com/arnaucube/fhe-study.git
synced 2026-01-23 20:23:54 +01:00
add ciphertext-by-const (plaintext) addition & mult
This commit is contained in:
@@ -85,6 +85,11 @@ impl<const Q: u64, const N: usize> PR<Q, N> {
|
||||
evals: None,
|
||||
})
|
||||
}
|
||||
// Warning: this method assumes Q < P
|
||||
pub fn remodule<const P: u64>(&self) -> PR<P, N> {
|
||||
assert!(Q < P);
|
||||
PR::<P, N>::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<Vec<Zq<Q>>>) -> Result<Vec<Zq<Q>>> {
|
||||
|
||||
106
bfv/src/lib.rs
106
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<const Q: u64, const N: usize> RLWE<Q, N> {
|
||||
fn add(lhs: Self, rhs: Self) -> Self {
|
||||
RLWE::<Q, N>(lhs.0 + rhs.0, lhs.1 + rhs.1)
|
||||
}
|
||||
fn mul(lhs: Self, rhs: Self) -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const Q: u64, const N: usize> ops::Add<RLWE<Q, N>> for RLWE<Q, N> {
|
||||
@@ -42,6 +39,21 @@ impl<const Q: u64, const N: usize> ops::Add<RLWE<Q, N>> for RLWE<Q, N> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<const Q: u64, const N: usize, const T: u64> ops::Add<&PR<T, N>> for &RLWE<Q, N> {
|
||||
type Output = RLWE<Q, N>;
|
||||
fn add(self, rhs: &PR<T, N>) -> Self::Output {
|
||||
// todo!()
|
||||
BFV::<Q, N, T>::add_const(self, rhs)
|
||||
}
|
||||
}
|
||||
impl<const Q: u64, const N: usize, const T: u64> ops::Mul<&PR<T, N>> for &RLWE<Q, N> {
|
||||
type Output = RLWE<Q, N>;
|
||||
fn mul(self, rhs: &PR<T, N>) -> Self::Output {
|
||||
// todo!()
|
||||
BFV::<Q, N, T>::mul_const(&self, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BFV<const Q: u64, const N: usize, const T: u64> {}
|
||||
|
||||
impl<const Q: u64, const N: usize, const T: u64> BFV<Q, N, T> {
|
||||
@@ -92,6 +104,17 @@ impl<const Q: u64, const N: usize, const T: u64> BFV<Q, N, T> {
|
||||
.collect();
|
||||
PR::<T, N>::from_vec_u64(r)
|
||||
}
|
||||
|
||||
fn add_const(c: &RLWE<Q, N>, m: &PR<T, N>) -> RLWE<Q, N> {
|
||||
// assuming T<Q, move m from Zq<T> to Zq<Q>
|
||||
let m = m.remodule::<Q>();
|
||||
RLWE::<Q, N>(c.0 + m * Self::DELTA, c.1)
|
||||
}
|
||||
fn mul_const(c: &RLWE<Q, N>, m: &PR<T, N>) -> RLWE<Q, N> {
|
||||
// assuming T<Q, move m from Zq<T> to Zq<Q>
|
||||
let m = m.remodule::<Q>();
|
||||
RLWE::<Q, N>(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<Q, N, T>;
|
||||
|
||||
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::<T, N>::rand_u64(&mut rng, msg_dist)?;
|
||||
let m2_const = PR::<T, N>::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::<T>(&m1.coeffs().to_vec(), &m2_const.coeffs().to_vec());
|
||||
arithmetic::ring::modulus::<T, N>(&mut mul_res);
|
||||
dbg!(&mul_res);
|
||||
let mul_res_2 =
|
||||
naive_poly_mul_2::<T, N>(&m1.coeffs().to_vec(), &m2_const.coeffs().to_vec());
|
||||
assert_eq!(mul_res, mul_res_2);
|
||||
let mul_res = PR::<T, N>::from_vec(mul_res);
|
||||
assert_eq!(mul_res.coeffs(), m3_mul_recovered.coeffs());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn naive_poly_mul<const T: u64>(a: &[Zq<T>], b: &[Zq<T>]) -> Vec<Zq<T>> {
|
||||
let mut result: Vec<Zq<T>> = 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<const T: u64, const N: usize>(
|
||||
poly1: &[Zq<T>],
|
||||
poly2: &[Zq<T>],
|
||||
) -> Vec<Zq<T>> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user