(TFHE): add TLWE encryption & decryption

This commit is contained in:
2025-07-22 14:59:56 +00:00
parent d60eb1dff1
commit 0ca73ac505
12 changed files with 274 additions and 100 deletions

View File

@@ -30,4 +30,10 @@ pub trait Ring:
fn from_vec(coeffs: Vec<Self::C>) -> Self;
fn decompose(&self, beta: u32, l: u32) -> Vec<Self>;
/// returns [ [(num/den) * self].round() ] mod q
/// ie. performs the multiplication and division over f64, and then it
/// rounds the result, only applying the mod Q (if the ring is mod Q) at the
/// end.
fn mul_div_round(&self, num: u64, den: u64) -> Self;
}

View File

@@ -43,6 +43,19 @@ impl<const N: usize> Ring for R<N> {
unimplemented!();
// array::from_fn(|i| self.coeffs[i].decompose(beta, l))
}
// performs the multiplication and division over f64, and then it rounds the
// result, only applying the mod Q at the end
fn mul_div_round(&self, num: u64, den: u64) -> Self {
unimplemented!()
// fn mul_div_round<const Q: u64>(&self, num: u64, den: u64) -> crate::Rq<Q, N> {
// let r: Vec<f64> = self
// .coeffs()
// .iter()
// .map(|e| ((num as f64 * *e as f64) / den as f64).round())
// .collect();
// crate::Rq::<Q, N>::from_vec_f64(r)
}
}
impl<const Q: u64, const N: usize> From<crate::ring_nq::Rq<Q, N>> for R<N> {
@@ -74,16 +87,6 @@ impl<const N: usize> R<N> {
pub fn mul_by_i64(&self, s: i64) -> Self {
Self(array::from_fn(|i| self.0[i] * s))
}
// performs the multiplication and division over f64, and then it rounds the
// result, only applying the mod Q at the end
pub fn mul_div_round<const Q: u64>(&self, num: u64, den: u64) -> crate::Rq<Q, N> {
let r: Vec<f64> = self
.coeffs()
.iter()
.map(|e| ((num as f64 * *e as f64) / den as f64).round())
.collect();
crate::Rq::<Q, N>::from_vec_f64(r)
}
pub fn infinity_norm(&self) -> u64 {
self.coeffs()

View File

@@ -70,6 +70,18 @@ impl<const Q: u64, const N: usize> Ring for Rq<Q, N> {
// convert it to Rq<Q,N>
r.iter().map(|a_i| Self::from_vec(a_i.clone())).collect()
}
// returns [ [(num/den) * self].round() ] mod q
// ie. performs the multiplication and division over f64, and then it rounds the
// result, only applying the mod Q at the end
fn mul_div_round(&self, num: u64, den: u64) -> Self {
let r: Vec<f64> = self
.coeffs()
.iter()
.map(|e| ((num as f64 * e.0 as f64) / den as f64).round())
.collect();
Rq::<Q, N>::from_vec_f64(r)
}
}
impl<const Q: u64, const N: usize> From<crate::ring_n::R<N>> for Rq<Q, N> {
@@ -231,17 +243,6 @@ impl<const Q: u64, const N: usize> Rq<Q, N> {
.collect();
Rq::<Q, N>::from_vec_f64(r)
}
// returns [ [(num/den) * self].round() ] mod q
// ie. performs the multiplication and division over f64, and then it rounds the
// result, only applying the mod Q at the end
pub fn mul_div_round(&self, num: u64, den: u64) -> Self {
let r: Vec<f64> = self
.coeffs()
.iter()
.map(|e| ((num as f64 * e.0 as f64) / den as f64).round())
.collect();
Rq::<Q, N>::from_vec_f64(r)
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// TODO simplify

View File

@@ -49,6 +49,18 @@ impl<const N: usize> Ring for Tn<N> {
// convert it to Tn<N>
r.iter().map(|a_i| Self::from_vec(a_i.clone())).collect()
}
/// returns [ [(num/den) * self].round() ] mod q
/// ie. performs the multiplication and division over f64, and then it rounds the
/// result, only applying the mod Q at the end
fn mul_div_round(&self, num: u64, den: u64) -> Self {
let r: Vec<T64> = self
.coeffs()
.iter()
.map(|e| T64(((num as f64 * e.0 as f64) / den as f64).round() as u64))
.collect();
Self::from_vec(r)
}
}
// apply mod (X^N+1)

View File

@@ -1,12 +1,15 @@
//! This file implements the struct for an Tuple of Ring Rq elements and its
//! operations.
//! operations, which are performed element-wise.
use anyhow::Result;
use itertools::zip_eq;
use rand::{distributions::Distribution, Rng};
use rand_distr::{Normal, Uniform};
use std::iter::Sum;
use std::{array, ops};
use std::{
array,
ops::{Add, Mul, Sub},
};
use crate::Ring;
@@ -34,7 +37,7 @@ impl<R: Ring, const K: usize> TR<R, K> {
}
}
impl<R: Ring, const K: usize> ops::Add<TR<R, K>> for TR<R, K> {
impl<R: Ring, const K: usize> Add<TR<R, K>> for TR<R, K> {
type Output = Self;
fn add(self, other: Self) -> Self {
Self(
@@ -45,7 +48,7 @@ impl<R: Ring, const K: usize> ops::Add<TR<R, K>> for TR<R, K> {
}
}
impl<R: Ring, const K: usize> ops::Sub<TR<R, K>> for TR<R, K> {
impl<R: Ring, const K: usize> Sub<TR<R, K>> for TR<R, K> {
type Output = Self;
fn sub(self, other: Self) -> Self {
Self(zip_eq(self.0, other.0).map(|(s, o)| s - o).collect())
@@ -54,13 +57,13 @@ impl<R: Ring, const K: usize> ops::Sub<TR<R, K>> for TR<R, K> {
/// for (TR,TR), the Mul operation is defined as:
/// for A, B \in R^k, result = Σ A_i * B_i \in R
impl<R: Ring, const K: usize> ops::Mul<TR<R, K>> for TR<R, K> {
impl<R: Ring, const K: usize> Mul<TR<R, K>> for TR<R, K> {
type Output = R;
fn mul(self, other: Self) -> R {
zip_eq(self.0, other.0).map(|(s, o)| s * o).sum()
}
}
impl<R: Ring, const K: usize> ops::Mul<&TR<R, K>> for &TR<R, K> {
impl<R: Ring, const K: usize> Mul<&TR<R, K>> for &TR<R, K> {
type Output = R;
fn mul(self, other: &TR<R, K>) -> R {
zip_eq(self.0.clone(), other.0.clone())
@@ -71,13 +74,13 @@ impl<R: Ring, const K: usize> ops::Mul<&TR<R, K>> for &TR<R, K> {
/// for (TR, R), the Mul operation is defined as each element of TR is
/// multiplied by R
impl<R: Ring, const K: usize> ops::Mul<R> for TR<R, K> {
impl<R: Ring, const K: usize> Mul<R> for TR<R, K> {
type Output = TR<R, K>;
fn mul(self, other: R) -> TR<R, K> {
Self(self.0.iter().map(|s| s.clone() * other.clone()).collect())
}
}
impl<R: Ring, const K: usize> ops::Mul<&R> for &TR<R, K> {
impl<R: Ring, const K: usize> Mul<&R> for &TR<R, K> {
type Output = TR<R, K>;
fn mul(self, other: &R) -> TR<R, K> {
TR::<R, K>(self.0.iter().map(|s| s.clone() * other.clone()).collect())