|
|
@ -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::<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 ops::Add> for RLWE { |
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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 BFV { |
|
|
|
.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
|
|
|
|
}
|
|
|
|
}
|