mirror of
https://github.com/arnaucube/phantom-zone.git
synced 2026-01-09 23:51:30 +01:00
add bool pbs
This commit is contained in:
@@ -12,6 +12,12 @@ pub trait VectorOps {
|
||||
fn elwise_neg_mut(&self, a: &mut [Self::Element]);
|
||||
/// inplace mutates `a`: a = a + b*c
|
||||
fn elwise_fma_mut(&self, a: &mut [Self::Element], b: &[Self::Element], c: &[Self::Element]);
|
||||
fn elwise_fma_scalar_mut(
|
||||
&self,
|
||||
a: &mut [Self::Element],
|
||||
b: &[Self::Element],
|
||||
c: &Self::Element,
|
||||
);
|
||||
|
||||
fn modulus(&self) -> Self::Element;
|
||||
}
|
||||
@@ -169,6 +175,17 @@ impl VectorOps for ModularOpsU64 {
|
||||
});
|
||||
}
|
||||
|
||||
fn elwise_fma_scalar_mut(
|
||||
&self,
|
||||
a: &mut [Self::Element],
|
||||
b: &[Self::Element],
|
||||
c: &Self::Element,
|
||||
) {
|
||||
izip!(a.iter_mut(), b.iter()).for_each(|(ai, bi)| {
|
||||
*ai = self.add_mod_fast(*ai, self.mul_mod_fast(*bi, *c));
|
||||
});
|
||||
}
|
||||
|
||||
fn modulus(&self) -> Self::Element {
|
||||
self.q
|
||||
}
|
||||
|
||||
360
src/bool.rs
Normal file
360
src/bool.rs
Normal file
@@ -0,0 +1,360 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use num_traits::{FromPrimitive, One, PrimInt, ToPrimitive, Zero};
|
||||
|
||||
use crate::{
|
||||
backend::{ArithmeticOps, VectorOps},
|
||||
decomposer::Decomposer,
|
||||
lwe::lwe_key_switch,
|
||||
ntt::Ntt,
|
||||
rgsw::{galois_auto, rlwe_by_rgsw, IsTrivial},
|
||||
Matrix, MatrixEntity, MatrixMut, Row, RowMut,
|
||||
};
|
||||
|
||||
struct BoolEvaluator {}
|
||||
|
||||
impl BoolEvaluator {}
|
||||
|
||||
trait PbsKey {
|
||||
type M: Matrix;
|
||||
|
||||
fn rgsw_ct_secret_el(&self, si: usize) -> &Self::M;
|
||||
fn galois_key_for_auto(&self, k: isize) -> &Self::M;
|
||||
fn auto_map_index(&self, k: isize) -> &[usize];
|
||||
fn auto_map_sign(&self, k: isize) -> &[bool];
|
||||
}
|
||||
|
||||
/// LMKCY+ Blind rotation
|
||||
///
|
||||
/// gk_to_si: [-g^0, -g^1, .., -g^{q/2-1}, g^0, ..., g^{q/2-1}]
|
||||
fn blind_rotation<
|
||||
MT: IsTrivial + MatrixMut,
|
||||
Mmut: MatrixMut<MatElement = MT::MatElement> + Matrix,
|
||||
D: Decomposer<Element = MT::MatElement>,
|
||||
NttOp: Ntt<Element = MT::MatElement>,
|
||||
ModOp: ArithmeticOps<Element = MT::MatElement> + VectorOps<Element = MT::MatElement>,
|
||||
K: PbsKey<M = Mmut>,
|
||||
>(
|
||||
trivial_rlwe_test_poly: &mut MT,
|
||||
scratch_matrix_dplus2_ring: &mut Mmut,
|
||||
g: isize,
|
||||
w: usize,
|
||||
q: usize,
|
||||
gk_to_si: &[Vec<usize>],
|
||||
decomposer: &D,
|
||||
ntt_op: &NttOp,
|
||||
mod_op: &ModOp,
|
||||
pbs_key: &K,
|
||||
) where
|
||||
<Mmut as Matrix>::R: RowMut,
|
||||
Mmut::MatElement: Copy + Zero,
|
||||
<MT as Matrix>::R: RowMut,
|
||||
{
|
||||
let q_by_2 = q / 2;
|
||||
|
||||
// -(g^k)
|
||||
for i in 1..q_by_2 {
|
||||
gk_to_si[q_by_2 + i].iter().for_each(|s_index| {
|
||||
rlwe_by_rgsw(
|
||||
trivial_rlwe_test_poly,
|
||||
pbs_key.rgsw_ct_secret_el(*s_index),
|
||||
scratch_matrix_dplus2_ring,
|
||||
decomposer,
|
||||
ntt_op,
|
||||
mod_op,
|
||||
);
|
||||
});
|
||||
|
||||
galois_auto(
|
||||
trivial_rlwe_test_poly,
|
||||
pbs_key.galois_key_for_auto(g),
|
||||
scratch_matrix_dplus2_ring,
|
||||
pbs_key.auto_map_index(g),
|
||||
pbs_key.auto_map_sign(g),
|
||||
mod_op,
|
||||
ntt_op,
|
||||
decomposer,
|
||||
);
|
||||
}
|
||||
|
||||
// -(g^0)
|
||||
gk_to_si[q_by_2].iter().for_each(|s_index| {
|
||||
rlwe_by_rgsw(
|
||||
trivial_rlwe_test_poly,
|
||||
pbs_key.rgsw_ct_secret_el(*s_index),
|
||||
scratch_matrix_dplus2_ring,
|
||||
decomposer,
|
||||
ntt_op,
|
||||
mod_op,
|
||||
);
|
||||
});
|
||||
galois_auto(
|
||||
trivial_rlwe_test_poly,
|
||||
pbs_key.galois_key_for_auto(-g),
|
||||
scratch_matrix_dplus2_ring,
|
||||
pbs_key.auto_map_index(-g),
|
||||
pbs_key.auto_map_sign(-g),
|
||||
mod_op,
|
||||
ntt_op,
|
||||
decomposer,
|
||||
);
|
||||
|
||||
// +(g^k)
|
||||
for i in 1..q_by_2 {
|
||||
gk_to_si[i].iter().for_each(|s_index| {
|
||||
rlwe_by_rgsw(
|
||||
trivial_rlwe_test_poly,
|
||||
pbs_key.rgsw_ct_secret_el(*s_index),
|
||||
scratch_matrix_dplus2_ring,
|
||||
decomposer,
|
||||
ntt_op,
|
||||
mod_op,
|
||||
);
|
||||
});
|
||||
|
||||
galois_auto(
|
||||
trivial_rlwe_test_poly,
|
||||
pbs_key.galois_key_for_auto(g),
|
||||
scratch_matrix_dplus2_ring,
|
||||
pbs_key.auto_map_index(g),
|
||||
pbs_key.auto_map_sign(g),
|
||||
mod_op,
|
||||
ntt_op,
|
||||
decomposer,
|
||||
);
|
||||
}
|
||||
|
||||
// +(g^0)
|
||||
gk_to_si[0].iter().for_each(|s_index| {
|
||||
rlwe_by_rgsw(
|
||||
trivial_rlwe_test_poly,
|
||||
pbs_key.rgsw_ct_secret_el(gk_to_si[q_by_2][*s_index]),
|
||||
scratch_matrix_dplus2_ring,
|
||||
decomposer,
|
||||
ntt_op,
|
||||
mod_op,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
trait Parameters {
|
||||
type Element;
|
||||
type D: Decomposer<Element = Self::Element>;
|
||||
fn rlwe_q(&self) -> Self::Element;
|
||||
fn lwe_q(&self) -> Self::Element;
|
||||
fn br_q(&self) -> usize;
|
||||
fn d_rgsw(&self) -> usize;
|
||||
fn d_lwe(&self) -> usize;
|
||||
fn rlwe_n(&self) -> usize;
|
||||
fn lwe_n(&self) -> usize;
|
||||
// Embedding fator for ring X^{q}+1 inside
|
||||
fn embedding_factor(&self) -> usize;
|
||||
// generator g
|
||||
fn g(&self) -> isize;
|
||||
fn decomoposer_lwe(&self) -> &Self::D;
|
||||
fn decomoposer_rlwe(&self) -> &Self::D;
|
||||
/// Maps a \in Z^*_{2q} to discrete log k, with generator g (i.e. g^k =
|
||||
/// a). Returned vector is of size q that stores dlog of a at `vec[a]`.
|
||||
/// For any a, k is s.t. a = g^{k}, then k is expressed as k. If k is s.t a
|
||||
/// = -g^{k/2}, then k is expressed as k=k+q/2
|
||||
fn g_k_dlog_map(&self) -> &[usize];
|
||||
}
|
||||
|
||||
/// - Mod down
|
||||
/// - key switching
|
||||
/// - mod down
|
||||
/// - blind rotate
|
||||
fn pbs<
|
||||
M: Matrix + MatrixMut + MatrixEntity,
|
||||
MT: MatrixMut<MatElement = M::MatElement, R = M::R> + IsTrivial + MatrixEntity,
|
||||
P: Parameters<Element = M::MatElement>,
|
||||
NttOp: Ntt<Element = M::MatElement>,
|
||||
ModOp: ArithmeticOps<Element = M::MatElement> + VectorOps<Element = M::MatElement>,
|
||||
K: PbsKey<M = M>,
|
||||
>(
|
||||
parameters: &P,
|
||||
test_vec: &M::R,
|
||||
lwe_in: &mut M::R,
|
||||
lwe_ksk: &M,
|
||||
scratch_lwen_plus1: &mut M::R,
|
||||
scratch_matrix_dplus2_ring: &mut M,
|
||||
modop_lweq: &ModOp,
|
||||
modop_rlweq: &ModOp,
|
||||
nttop_rlweq: &NttOp,
|
||||
pbs_key: K,
|
||||
) where
|
||||
<M as Matrix>::R: RowMut,
|
||||
<MT as Matrix>::R: RowMut,
|
||||
M::MatElement: PrimInt + ToPrimitive + FromPrimitive + One + Copy + Zero,
|
||||
{
|
||||
let rlwe_q = parameters.rlwe_q();
|
||||
let lwe_q = parameters.lwe_q();
|
||||
let br_q = parameters.br_q();
|
||||
let rlwe_qf64 = rlwe_q.to_f64().unwrap();
|
||||
let lwe_qf64 = lwe_q.to_f64().unwrap();
|
||||
let br_qf64 = br_q.to_f64().unwrap();
|
||||
let rlwe_n = parameters.rlwe_n();
|
||||
|
||||
// moddown Q -> Q_ks
|
||||
lwe_in.as_mut().iter_mut().for_each(|v| {
|
||||
*v =
|
||||
M::MatElement::from_f64(((v.to_f64().unwrap() * lwe_qf64) / rlwe_qf64).round()).unwrap()
|
||||
});
|
||||
|
||||
// key switch
|
||||
// let mut lwe_out = M::zeros(1, parameters.lwe_n() + 1);
|
||||
scratch_lwen_plus1.as_mut().fill(M::MatElement::zero());
|
||||
lwe_key_switch(
|
||||
scratch_lwen_plus1,
|
||||
lwe_in,
|
||||
lwe_ksk,
|
||||
modop_lweq,
|
||||
parameters.decomoposer_lwe(),
|
||||
);
|
||||
|
||||
// odd mowdown Q_ks -> q
|
||||
let g_k_dlog_map = parameters.g_k_dlog_map();
|
||||
let mut g_k_si = vec![vec![]; br_q];
|
||||
scratch_lwen_plus1
|
||||
.as_ref()
|
||||
.iter()
|
||||
.skip(1)
|
||||
.enumerate()
|
||||
.for_each(|(index, v)| {
|
||||
let odd_v = mod_switch_odd(v.to_f64().unwrap(), lwe_qf64, br_qf64);
|
||||
let k = g_k_dlog_map[odd_v];
|
||||
g_k_si[k].push(index);
|
||||
});
|
||||
|
||||
// handle b and set trivial test RLWE
|
||||
let g = parameters.g() as usize;
|
||||
let g_times_b = (g * mod_switch_odd(
|
||||
scratch_lwen_plus1.as_ref()[0].to_f64().unwrap(),
|
||||
lwe_qf64,
|
||||
br_qf64,
|
||||
)) % (br_q);
|
||||
// v = (v(X) * X^{g*b}) mod X^{q/2}+1
|
||||
let br_qby2 = br_q / 2;
|
||||
let mut gb_monomial_sign = true;
|
||||
let mut gb_monomial_exp = g_times_b;
|
||||
// X^{g*b} mod X^{q}+1
|
||||
if gb_monomial_exp > br_qby2 {
|
||||
gb_monomial_exp -= br_qby2;
|
||||
gb_monomial_sign = false
|
||||
}
|
||||
// monomial mul
|
||||
let mut trivial_rlwe_test_poly = MT::zeros(2, rlwe_n);
|
||||
if parameters.embedding_factor() == 1 {
|
||||
monomial_mul(
|
||||
test_vec.as_ref(),
|
||||
trivial_rlwe_test_poly.get_row_mut(1).as_mut(),
|
||||
gb_monomial_exp,
|
||||
gb_monomial_sign,
|
||||
br_q,
|
||||
modop_rlweq,
|
||||
);
|
||||
} else {
|
||||
// use lwe_in to store the `t = v(X) * X^{g*2} mod X^{q/2}+1` temporarily. This
|
||||
// works because q/2 < N (where N is lwe_in LWE dimension) always.
|
||||
monomial_mul(
|
||||
test_vec.as_ref(),
|
||||
&mut lwe_in.as_mut()[..br_qby2],
|
||||
gb_monomial_exp,
|
||||
gb_monomial_sign,
|
||||
br_q,
|
||||
modop_rlweq,
|
||||
);
|
||||
|
||||
// emebed poly `t` in ring X^{q/2}+1 inside the bigger ring X^{N}+1
|
||||
let partb_trivial_rlwe = trivial_rlwe_test_poly.get_row_mut(1);
|
||||
lwe_in.as_ref()[..br_qby2]
|
||||
.iter()
|
||||
.enumerate()
|
||||
.for_each(|(index, v)| {
|
||||
partb_trivial_rlwe[2 * index] = *v;
|
||||
});
|
||||
}
|
||||
// TODO Rotate test input
|
||||
|
||||
// blind rotate
|
||||
blind_rotation(
|
||||
&mut trivial_rlwe_test_poly,
|
||||
scratch_matrix_dplus2_ring,
|
||||
parameters.g(),
|
||||
1,
|
||||
br_q,
|
||||
&g_k_si,
|
||||
parameters.decomoposer_rlwe(),
|
||||
nttop_rlweq,
|
||||
modop_rlweq,
|
||||
&pbs_key,
|
||||
);
|
||||
|
||||
// sample extract
|
||||
sample_extract(lwe_in, &trivial_rlwe_test_poly, modop_rlweq, 0);
|
||||
}
|
||||
|
||||
fn mod_switch_odd(v: f64, from_q: f64, to_q: f64) -> usize {
|
||||
let odd_v = (((v.to_f64().unwrap() * to_q) / (from_q)).floor())
|
||||
.to_usize()
|
||||
.unwrap();
|
||||
//TODO(Jay): check correctness of this
|
||||
odd_v + (odd_v ^ (usize::one()))
|
||||
}
|
||||
|
||||
fn sample_extract<M: Matrix + MatrixMut, ModOp: ArithmeticOps<Element = M::MatElement>>(
|
||||
lwe_out: &mut M::R,
|
||||
rlwe_in: &M,
|
||||
mod_op: &ModOp,
|
||||
index: usize,
|
||||
) where
|
||||
<M as Matrix>::R: RowMut,
|
||||
M::MatElement: Copy,
|
||||
{
|
||||
let ring_size = rlwe_in.dimension().1;
|
||||
|
||||
// index..=0
|
||||
let to = &mut lwe_out.as_mut()[1..];
|
||||
let from = rlwe_in.get_row_slice(0);
|
||||
for i in 0..index + 1 {
|
||||
to[i] = from[index - i];
|
||||
}
|
||||
|
||||
// -(N..index)
|
||||
for i in index + 1..ring_size {
|
||||
to[i] = mod_op.neg(&from[ring_size + index - i]);
|
||||
}
|
||||
|
||||
// set b
|
||||
lwe_out.as_mut()[0] = *rlwe_in.get(1, index);
|
||||
}
|
||||
|
||||
fn monomial_mul<El, ModOp: ArithmeticOps<Element = El>>(
|
||||
p_in: &[El],
|
||||
p_out: &mut [El],
|
||||
mon_exp: usize,
|
||||
mon_sign: bool,
|
||||
ring_size: usize,
|
||||
mod_op: &ModOp,
|
||||
) where
|
||||
El: Copy,
|
||||
{
|
||||
debug_assert!(p_in.as_ref().len() == ring_size);
|
||||
debug_assert!(p_in.as_ref().len() == p_out.as_ref().len());
|
||||
debug_assert!(mon_exp < ring_size);
|
||||
|
||||
p_in.as_ref().iter().enumerate().for_each(|(index, v)| {
|
||||
let mut to_index = index + mon_exp;
|
||||
let mut to_sign = mon_sign;
|
||||
if to_index >= ring_size {
|
||||
to_index = to_index - ring_size;
|
||||
to_sign = !to_sign;
|
||||
}
|
||||
|
||||
if !to_sign {
|
||||
p_out.as_mut()[to_index] = mod_op.neg(v);
|
||||
} else {
|
||||
p_out.as_mut()[to_index] = *v;
|
||||
}
|
||||
});
|
||||
}
|
||||
26
src/lib.rs
26
src/lib.rs
@@ -6,6 +6,7 @@ use random::{RandomGaussianDist, RandomUniformDist};
|
||||
use utils::TryConvertFrom;
|
||||
|
||||
mod backend;
|
||||
mod bool;
|
||||
mod decomposer;
|
||||
mod lwe;
|
||||
mod ntt;
|
||||
@@ -34,6 +35,10 @@ pub trait Matrix: AsRef<[Self::R]> {
|
||||
fn get(&self, row_idx: usize, column_idx: usize) -> &Self::MatElement {
|
||||
&self.as_ref()[row_idx].as_ref()[column_idx]
|
||||
}
|
||||
|
||||
fn split_at_row(&self, idx: usize) -> (&[<Self as Matrix>::R], &[<Self as Matrix>::R]) {
|
||||
self.as_ref().split_at(idx)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait MatrixMut: Matrix + AsMut<[<Self as Matrix>::R]>
|
||||
@@ -52,7 +57,7 @@ where
|
||||
self.as_mut()[row_idx].as_mut()[column_idx] = val;
|
||||
}
|
||||
|
||||
fn split_at_row(
|
||||
fn split_at_row_mut(
|
||||
&mut self,
|
||||
idx: usize,
|
||||
) -> (&mut [<Self as Matrix>::R], &mut [<Self as Matrix>::R]) {
|
||||
@@ -86,7 +91,26 @@ impl<T> Matrix for Vec<Vec<T>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Matrix for &[Vec<T>] {
|
||||
type MatElement = T;
|
||||
type R = Vec<T>;
|
||||
|
||||
fn dimension(&self) -> (usize, usize) {
|
||||
(self.len(), self[0].len())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Matrix for &mut [Vec<T>] {
|
||||
type MatElement = T;
|
||||
type R = Vec<T>;
|
||||
|
||||
fn dimension(&self) -> (usize, usize) {
|
||||
(self.len(), self[0].len())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> MatrixMut for Vec<Vec<T>> {}
|
||||
impl<T> MatrixMut for &mut [Vec<T>] {}
|
||||
|
||||
impl<T: Zero + Clone> MatrixEntity for Vec<Vec<T>> {
|
||||
fn zeros(row: usize, col: usize) -> Self {
|
||||
|
||||
110
src/lwe.rs
110
src/lwe.rs
@@ -43,36 +43,32 @@ impl LweSecret {
|
||||
}
|
||||
}
|
||||
|
||||
fn lwe_key_switch<
|
||||
pub(crate) fn lwe_key_switch<
|
||||
M: Matrix,
|
||||
Mmut: MatrixMut<MatElement = M::MatElement> + MatrixEntity,
|
||||
Ro: AsMut<[M::MatElement]> + AsRef<[M::MatElement]>,
|
||||
Op: VectorOps<Element = M::MatElement> + ArithmeticOps<Element = M::MatElement>,
|
||||
D: Decomposer<Element = M::MatElement>,
|
||||
>(
|
||||
lwe_out: &mut Mmut,
|
||||
lwe_in: &M,
|
||||
lwe_out: &mut Ro,
|
||||
lwe_in: &Ro,
|
||||
lwe_ksk: &M,
|
||||
operator: &Op,
|
||||
decomposer: &D,
|
||||
) where
|
||||
<Mmut as Matrix>::R: RowMut,
|
||||
{
|
||||
assert!(lwe_ksk.dimension().0 == ((lwe_in.dimension().1 - 1) * decomposer.d()));
|
||||
assert!(lwe_out.dimension() == (1, lwe_ksk.dimension().1));
|
||||
|
||||
let mut scratch_space = Mmut::zeros(1, lwe_out.dimension().1);
|
||||
) {
|
||||
assert!(lwe_ksk.dimension().0 == ((lwe_in.as_ref().len() - 1) * decomposer.d()));
|
||||
assert!(lwe_out.as_ref().len() == lwe_ksk.dimension().1);
|
||||
|
||||
let lwe_in_a_decomposed = lwe_in
|
||||
.get_row(0)
|
||||
.as_ref()
|
||||
.iter()
|
||||
.skip(1)
|
||||
.flat_map(|ai| decomposer.decompose(ai));
|
||||
izip!(lwe_in_a_decomposed, lwe_ksk.iter_rows()).for_each(|(ai_j, beta_ij_lwe)| {
|
||||
operator.elwise_scalar_mul(scratch_space.get_row_mut(0), beta_ij_lwe.as_ref(), &ai_j);
|
||||
operator.elwise_add_mut(lwe_out.get_row_mut(0), scratch_space.get_row_slice(0))
|
||||
operator.elwise_fma_scalar_mut(lwe_out.as_mut(), beta_ij_lwe.as_ref(), &ai_j);
|
||||
});
|
||||
|
||||
let out_b = operator.add(lwe_out.get(0, 0), lwe_in.get(0, 0));
|
||||
lwe_out.set(0, 0, out_b);
|
||||
let out_b = operator.add(&lwe_out.as_ref()[0], &lwe_in.as_ref()[0]);
|
||||
lwe_out.as_mut()[0] = out_b;
|
||||
}
|
||||
|
||||
fn lwe_ksk_keygen<
|
||||
@@ -82,34 +78,34 @@ fn lwe_ksk_keygen<
|
||||
R: RandomGaussianDist<Mmut::MatElement, Parameters = Mmut::MatElement>
|
||||
+ RandomUniformDist<[Mmut::MatElement], Parameters = Mmut::MatElement>,
|
||||
>(
|
||||
lwe_sk_in: &S,
|
||||
lwe_sk_out: &S,
|
||||
from_lwe_sk: &S,
|
||||
to_lwe_sk: &S,
|
||||
ksk_out: &mut Mmut,
|
||||
gadget: &[Mmut::MatElement],
|
||||
operator: &Op,
|
||||
rng: &mut R,
|
||||
) where
|
||||
<Mmut as Matrix>::R: RowMut,
|
||||
Mmut: TryConvertFrom<[S::Element], Parameters = Mmut::MatElement>,
|
||||
Mmut::R: TryConvertFrom<[S::Element], Parameters = Mmut::MatElement>,
|
||||
Mmut::MatElement: Zero + Debug,
|
||||
{
|
||||
assert!(
|
||||
ksk_out.dimension()
|
||||
== (
|
||||
lwe_sk_in.values().len() * gadget.len(),
|
||||
lwe_sk_out.values().len() + 1,
|
||||
from_lwe_sk.values().len() * gadget.len(),
|
||||
to_lwe_sk.values().len() + 1,
|
||||
)
|
||||
);
|
||||
|
||||
let d = gadget.len();
|
||||
|
||||
let modulus = VectorOps::modulus(operator);
|
||||
let mut neg_sk_in_m = Mmut::try_convert_from(lwe_sk_in.values(), &modulus);
|
||||
operator.elwise_neg_mut(neg_sk_in_m.get_row_mut(0));
|
||||
let sk_out_m = Mmut::try_convert_from(lwe_sk_out.values(), &modulus);
|
||||
let mut neg_sk_in_m = Mmut::R::try_convert_from(from_lwe_sk.values(), &modulus);
|
||||
operator.elwise_neg_mut(neg_sk_in_m.as_mut());
|
||||
let sk_out_m = Mmut::R::try_convert_from(to_lwe_sk.values(), &modulus);
|
||||
|
||||
izip!(
|
||||
neg_sk_in_m.get_row(0),
|
||||
neg_sk_in_m.as_ref(),
|
||||
ksk_out.iter_rows_mut().chunks(d).into_iter()
|
||||
)
|
||||
.for_each(|(neg_sk_in_si, d_ks_lwes)| {
|
||||
@@ -119,7 +115,7 @@ fn lwe_ksk_keygen<
|
||||
|
||||
// a * z
|
||||
let mut az = Mmut::MatElement::zero();
|
||||
izip!(lwe.as_ref()[1..].iter(), sk_out_m.get_row(0)).for_each(|(ai, si)| {
|
||||
izip!(lwe.as_ref()[1..].iter(), sk_out_m.as_ref()).for_each(|(ai, si)| {
|
||||
let ai_si = operator.mul(ai, si);
|
||||
az = operator.add(&az, &ai_si);
|
||||
});
|
||||
@@ -139,59 +135,57 @@ fn lwe_ksk_keygen<
|
||||
|
||||
/// Encrypts encoded message m as LWE ciphertext
|
||||
fn encrypt_lwe<
|
||||
Mmut: MatrixMut + MatrixEntity,
|
||||
R: RandomGaussianDist<Mmut::MatElement, Parameters = Mmut::MatElement>
|
||||
+ RandomUniformDist<[Mmut::MatElement], Parameters = Mmut::MatElement>,
|
||||
Ro: Row + RowMut,
|
||||
R: RandomGaussianDist<Ro::Element, Parameters = Ro::Element>
|
||||
+ RandomUniformDist<[Ro::Element], Parameters = Ro::Element>,
|
||||
S: Secret,
|
||||
Op: ArithmeticOps<Element = Mmut::MatElement>,
|
||||
Op: ArithmeticOps<Element = Ro::Element>,
|
||||
>(
|
||||
lwe_out: &mut Mmut,
|
||||
m: Mmut::MatElement,
|
||||
lwe_out: &mut Ro,
|
||||
m: &Ro::Element,
|
||||
s: &S,
|
||||
operator: &Op,
|
||||
rng: &mut R,
|
||||
) where
|
||||
Mmut: TryConvertFrom<[S::Element], Parameters = Mmut::MatElement>,
|
||||
Mmut::MatElement: Zero,
|
||||
<Mmut as Matrix>::R: RowMut,
|
||||
Ro: TryConvertFrom<[S::Element], Parameters = Ro::Element>,
|
||||
Ro::Element: Zero,
|
||||
{
|
||||
let s = Mmut::try_convert_from(s.values(), &operator.modulus());
|
||||
assert!(s.dimension().0 == (lwe_out.dimension().0));
|
||||
assert!(s.dimension().1 == (lwe_out.dimension().1 - 1));
|
||||
let s = Ro::try_convert_from(s.values(), &operator.modulus());
|
||||
assert!(s.as_ref().len() == (lwe_out.as_ref().len() - 1));
|
||||
|
||||
// a*s
|
||||
RandomUniformDist::random_fill(rng, &operator.modulus(), &mut lwe_out.get_row_mut(0)[1..]);
|
||||
let mut sa = Mmut::MatElement::zero();
|
||||
izip!(lwe_out.get_row(0).skip(1), s.get_row(0)).for_each(|(ai, si)| {
|
||||
RandomUniformDist::random_fill(rng, &operator.modulus(), &mut lwe_out.as_mut()[1..]);
|
||||
let mut sa = Ro::Element::zero();
|
||||
izip!(lwe_out.as_mut().iter().skip(1), s.as_ref()).for_each(|(ai, si)| {
|
||||
let tmp = operator.mul(ai, si);
|
||||
sa = operator.add(&tmp, &sa);
|
||||
});
|
||||
|
||||
// b = a*s + e + m
|
||||
let mut e = Mmut::MatElement::zero();
|
||||
let mut e = Ro::Element::zero();
|
||||
RandomGaussianDist::random_fill(rng, &operator.modulus(), &mut e);
|
||||
let b = operator.add(&operator.add(&sa, &e), &m);
|
||||
lwe_out.set(0, 0, b);
|
||||
let b = operator.add(&operator.add(&sa, &e), m);
|
||||
lwe_out.as_mut()[0] = b;
|
||||
}
|
||||
|
||||
fn decrypt_lwe<M: Matrix, Op: ArithmeticOps<Element = M::MatElement>, S: Secret>(
|
||||
lwe_ct: &M,
|
||||
fn decrypt_lwe<Ro: Row, Op: ArithmeticOps<Element = Ro::Element>, S: Secret>(
|
||||
lwe_ct: &Ro,
|
||||
s: &S,
|
||||
operator: &Op,
|
||||
) -> M::MatElement
|
||||
) -> Ro::Element
|
||||
where
|
||||
M: TryConvertFrom<[S::Element], Parameters = M::MatElement>,
|
||||
M::MatElement: Zero,
|
||||
Ro: TryConvertFrom<[S::Element], Parameters = Ro::Element>,
|
||||
Ro::Element: Zero,
|
||||
{
|
||||
let s = M::try_convert_from(s.values(), &operator.modulus());
|
||||
let s = Ro::try_convert_from(s.values(), &operator.modulus());
|
||||
|
||||
let mut sa = M::MatElement::zero();
|
||||
izip!(lwe_ct.get_row(0).skip(1), s.get_row(0)).for_each(|(ai, si)| {
|
||||
let mut sa = Ro::Element::zero();
|
||||
izip!(lwe_ct.as_ref().iter().skip(1), s.as_ref()).for_each(|(ai, si)| {
|
||||
let tmp = operator.mul(ai, si);
|
||||
sa = operator.add(&tmp, &sa);
|
||||
});
|
||||
|
||||
let b = &lwe_ct.get_row_slice(0)[0];
|
||||
let b = &lwe_ct.as_ref()[0];
|
||||
operator.sub(b, &sa)
|
||||
}
|
||||
|
||||
@@ -222,8 +216,8 @@ mod tests {
|
||||
// encrypt
|
||||
for m in 0..1u64 << logp {
|
||||
let encoded_m = m << (logq - logp);
|
||||
let mut lwe_ct = vec![vec![0u64; lwe_n + 1]];
|
||||
encrypt_lwe(&mut lwe_ct, encoded_m, &lwe_sk, &modq_op, &mut rng);
|
||||
let mut lwe_ct = vec![0u64; lwe_n + 1];
|
||||
encrypt_lwe(&mut lwe_ct, &encoded_m, &lwe_sk, &modq_op, &mut rng);
|
||||
let encoded_m_back = decrypt_lwe(&lwe_ct, &lwe_sk, &modq_op);
|
||||
let m_back = ((((encoded_m_back as f64) * ((1 << logp) as f64)) / q as f64).round()
|
||||
as u64)
|
||||
@@ -265,12 +259,12 @@ mod tests {
|
||||
for m in 0..(1 << logp) {
|
||||
// encrypt using lwe_sk_in
|
||||
let encoded_m = m << (logq - logp);
|
||||
let mut lwe_in_ct = vec![vec![0u64; lwe_in_n + 1]];
|
||||
encrypt_lwe(&mut lwe_in_ct, encoded_m, &lwe_sk_in, &modq_op, &mut rng);
|
||||
let mut lwe_in_ct = vec![0u64; lwe_in_n + 1];
|
||||
encrypt_lwe(&mut lwe_in_ct, &encoded_m, &lwe_sk_in, &modq_op, &mut rng);
|
||||
|
||||
// key switch from lwe_sk_in to lwe_sk_out
|
||||
let decomposer = DefaultDecomposer::new(1u64 << logq, logb, d_ks);
|
||||
let mut lwe_out_ct = vec![vec![0u64; lwe_out_n + 1]];
|
||||
let mut lwe_out_ct = vec![0u64; lwe_out_n + 1];
|
||||
lwe_key_switch(&mut lwe_out_ct, &lwe_in_ct, &ksk, &modq_op, &decomposer);
|
||||
|
||||
// decrypt lwe_out_ct using lwe_sk_out
|
||||
|
||||
389
src/rgsw.rs
389
src/rgsw.rs
@@ -1,10 +1,11 @@
|
||||
use std::{
|
||||
clone,
|
||||
fmt::Debug,
|
||||
ops::{Neg, Sub},
|
||||
};
|
||||
|
||||
use itertools::{izip, Itertools};
|
||||
use num_traits::{PrimInt, ToPrimitive};
|
||||
use num_traits::{PrimInt, ToPrimitive, Zero};
|
||||
|
||||
use crate::{
|
||||
backend::{ArithmeticOps, VectorOps},
|
||||
@@ -15,6 +16,48 @@ use crate::{
|
||||
Matrix, MatrixEntity, MatrixMut, RowMut, Secret,
|
||||
};
|
||||
|
||||
struct RlweCiphertext<M>(M, bool);
|
||||
|
||||
impl<M: Matrix> Matrix for RlweCiphertext<M> {
|
||||
type MatElement = M::MatElement;
|
||||
type R = M::R;
|
||||
|
||||
fn dimension(&self) -> (usize, usize) {
|
||||
self.0.dimension()
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: MatrixMut> MatrixMut for RlweCiphertext<M> where <M as Matrix>::R: RowMut {}
|
||||
|
||||
impl<M: Matrix> AsRef<[<M as Matrix>::R]> for RlweCiphertext<M> {
|
||||
fn as_ref(&self) -> &[<M as Matrix>::R] {
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: MatrixMut> AsMut<[<M as Matrix>::R]> for RlweCiphertext<M>
|
||||
where
|
||||
<M as Matrix>::R: RowMut,
|
||||
{
|
||||
fn as_mut(&mut self) -> &mut [<M as Matrix>::R] {
|
||||
self.0.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<M> IsTrivial for RlweCiphertext<M> {
|
||||
fn is_trivial(&self) -> bool {
|
||||
self.1
|
||||
}
|
||||
fn set_not_trivial(&mut self) {
|
||||
self.1 = false;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IsTrivial {
|
||||
fn is_trivial(&self) -> bool;
|
||||
fn set_not_trivial(&mut self);
|
||||
}
|
||||
|
||||
struct RlweSecret {
|
||||
values: Vec<i32>,
|
||||
}
|
||||
@@ -70,7 +113,7 @@ fn generate_auto_map(ring_size: usize, k: usize) -> (Vec<usize>, Vec<bool>) {
|
||||
/// - neg_from_s_eval: Negative of secret polynomial to key switch from in
|
||||
/// evaluation domain
|
||||
/// - to_s_eval: secret polynomial to key switch to in evalution domain.
|
||||
fn rlwe_ksk_gen<
|
||||
pub(crate) fn rlwe_ksk_gen<
|
||||
Mmut: MatrixMut + MatrixEntity,
|
||||
ModOp: ArithmeticOps<Element = Mmut::MatElement> + VectorOps<Element = Mmut::MatElement>,
|
||||
NttOp: Ntt<Element = Mmut::MatElement>,
|
||||
@@ -98,7 +141,7 @@ fn rlwe_ksk_gen<
|
||||
let mut scratch_space = Mmut::zeros(1, ring_size);
|
||||
|
||||
// RLWE'_{to_s}(-from_s)
|
||||
let (part_a, part_b) = ksk_out.split_at_row(d);
|
||||
let (part_a, part_b) = ksk_out.split_at_row_mut(d);
|
||||
izip!(part_a.iter_mut(), part_b.iter_mut(), gadget_vector.iter()).for_each(
|
||||
|(ai, bi, beta_i)| {
|
||||
// sample ai and transform to evaluation
|
||||
@@ -130,7 +173,7 @@ fn rlwe_ksk_gen<
|
||||
);
|
||||
}
|
||||
|
||||
fn galois_key_gen<
|
||||
pub(crate) fn galois_key_gen<
|
||||
Mmut: MatrixMut + MatrixEntity,
|
||||
ModOp: ArithmeticOps<Element = Mmut::MatElement> + VectorOps<Element = Mmut::MatElement>,
|
||||
NttOp: Ntt<Element = Mmut::MatElement>,
|
||||
@@ -179,17 +222,16 @@ fn galois_key_gen<
|
||||
}
|
||||
|
||||
/// Sends RLWE_{s}(X) -> RLWE_{s}(X^k) where k is some galois element
|
||||
fn galois_auto<
|
||||
M: Matrix,
|
||||
Mmut: MatrixMut<MatElement = M::MatElement>,
|
||||
ModOp: ArithmeticOps<Element = M::MatElement> + VectorOps<Element = M::MatElement>,
|
||||
NttOp: Ntt<Element = M::MatElement>,
|
||||
D: Decomposer<Element = M::MatElement>,
|
||||
pub(crate) fn galois_auto<
|
||||
MT: Matrix + IsTrivial + MatrixMut,
|
||||
Mmut: MatrixMut<MatElement = MT::MatElement>,
|
||||
ModOp: ArithmeticOps<Element = MT::MatElement> + VectorOps<Element = MT::MatElement>,
|
||||
NttOp: Ntt<Element = MT::MatElement>,
|
||||
D: Decomposer<Element = MT::MatElement>,
|
||||
>(
|
||||
rlwe_in: &M,
|
||||
ksk: &M,
|
||||
rlwe_out: &mut Mmut,
|
||||
a_rlwe_decomposed: &mut Mmut,
|
||||
rlwe_in: &mut MT,
|
||||
ksk: &Mmut,
|
||||
scratch_matrix_dplus2_ring: &mut Mmut,
|
||||
auto_map_index: &[usize],
|
||||
auto_map_sign: &[bool],
|
||||
mod_op: &ModOp,
|
||||
@@ -197,10 +239,13 @@ fn galois_auto<
|
||||
decomposer: &D,
|
||||
) where
|
||||
<Mmut as Matrix>::R: RowMut,
|
||||
M::MatElement: Copy,
|
||||
<MT as Matrix>::R: RowMut,
|
||||
MT::MatElement: Copy + Zero,
|
||||
{
|
||||
let d = decomposer.d();
|
||||
|
||||
let (scratch_matrix_d_ring, tmp_rlwe_out) = scratch_matrix_dplus2_ring.split_at_row_mut(d);
|
||||
|
||||
// send b(X) -> b(X^k)
|
||||
izip!(
|
||||
rlwe_in.get_row(1),
|
||||
@@ -209,45 +254,69 @@ fn galois_auto<
|
||||
)
|
||||
.for_each(|(el_in, to_index, sign)| {
|
||||
if !*sign {
|
||||
rlwe_out.set(1, *to_index, mod_op.neg(el_in));
|
||||
tmp_rlwe_out[1].as_mut()[*to_index] = mod_op.neg(el_in);
|
||||
} else {
|
||||
rlwe_out.set(1, *to_index, *el_in);
|
||||
tmp_rlwe_out[1].as_mut()[*to_index] = *el_in;
|
||||
// scratch_matrix_dplus2_ring.set(d + 1, *to_index, *el_in);
|
||||
}
|
||||
});
|
||||
|
||||
// send a(X) -> a(X^k) and decompose a(X^k)
|
||||
izip!(
|
||||
rlwe_in.get_row(0),
|
||||
auto_map_index.iter(),
|
||||
auto_map_sign.iter()
|
||||
)
|
||||
.for_each(|(el_in, to_index, sign)| {
|
||||
let el_out = if !*sign { mod_op.neg(el_in) } else { *el_in };
|
||||
if !rlwe_in.is_trivial() {
|
||||
// send a(X) -> a(X^k) and decompose a(X^k)
|
||||
izip!(
|
||||
rlwe_in.get_row(0),
|
||||
auto_map_index.iter(),
|
||||
auto_map_sign.iter()
|
||||
)
|
||||
.for_each(|(el_in, to_index, sign)| {
|
||||
let el_out = if !*sign { mod_op.neg(el_in) } else { *el_in };
|
||||
|
||||
let el_out_decomposed = decomposer.decompose(&el_out);
|
||||
for j in 0..d {
|
||||
a_rlwe_decomposed.set(j, *to_index, el_out_decomposed[j]);
|
||||
}
|
||||
});
|
||||
let el_out_decomposed = decomposer.decompose(&el_out);
|
||||
for j in 0..d {
|
||||
scratch_matrix_d_ring[j].as_mut()[*to_index] = el_out_decomposed[j];
|
||||
}
|
||||
});
|
||||
|
||||
// transform decomposed a(X^k) to evaluation domain
|
||||
a_rlwe_decomposed.iter_rows_mut().for_each(|r| {
|
||||
ntt_op.forward(r.as_mut());
|
||||
});
|
||||
// transform decomposed a(X^k) to evaluation domain
|
||||
scratch_matrix_d_ring.iter_mut().for_each(|r| {
|
||||
ntt_op.forward(r.as_mut());
|
||||
});
|
||||
|
||||
// key switch (a(X^k) * RLWE'(s(X^k)))
|
||||
izip!(a_rlwe_decomposed.iter_rows(), ksk.iter_rows().take(d)).for_each(|(a, b)| {
|
||||
mod_op.elwise_fma_mut(rlwe_out.get_row_mut(0), a.as_ref(), b.as_ref());
|
||||
});
|
||||
ntt_op.forward(rlwe_out.get_row_mut(1));
|
||||
izip!(a_rlwe_decomposed.iter_rows(), ksk.iter_rows().skip(d)).for_each(|(a, b)| {
|
||||
mod_op.elwise_fma_mut(rlwe_out.get_row_mut(1), a.as_ref(), b.as_ref());
|
||||
});
|
||||
// RLWE(m^k) = a', b'; RLWE(m) = a, b
|
||||
// key switch: (a * RLWE'(s(X^k)))
|
||||
let (ksk_a, ksk_b) = ksk.split_at_row(d);
|
||||
tmp_rlwe_out[0].as_mut().fill(Mmut::MatElement::zero());
|
||||
// a' = decomp<a> * RLWE'_A(s(X^k))
|
||||
routine::<Mmut, ModOp>(
|
||||
tmp_rlwe_out[0].as_mut(),
|
||||
scratch_matrix_d_ring,
|
||||
ksk_a,
|
||||
mod_op,
|
||||
);
|
||||
// send b(X^k) to evaluation domain
|
||||
ntt_op.forward(tmp_rlwe_out[1].as_mut());
|
||||
// b' = b(X^k)
|
||||
// b' += decomp<a(X^k)> * RLWE'_B(s(X^k))
|
||||
routine::<Mmut, ModOp>(
|
||||
tmp_rlwe_out[1].as_mut(),
|
||||
scratch_matrix_d_ring,
|
||||
ksk_b,
|
||||
mod_op,
|
||||
);
|
||||
|
||||
// transform RLWE(-s(X^k) * a(X^k)) to coefficient domain
|
||||
rlwe_out
|
||||
.iter_rows_mut()
|
||||
.for_each(|r| ntt_op.backward(r.as_mut()));
|
||||
// transform RLWE(m^k) to coefficient domain
|
||||
tmp_rlwe_out
|
||||
.iter_mut()
|
||||
.for_each(|r| ntt_op.backward(r.as_mut()));
|
||||
|
||||
rlwe_in
|
||||
.get_row_mut(0)
|
||||
.copy_from_slice(tmp_rlwe_out[0].as_ref());
|
||||
}
|
||||
|
||||
rlwe_in
|
||||
.get_row_mut(1)
|
||||
.copy_from_slice(tmp_rlwe_out[1].as_ref());
|
||||
}
|
||||
|
||||
/// Encrypts message m as a RGSW ciphertext.
|
||||
@@ -256,7 +325,7 @@ fn galois_auto<
|
||||
/// - out_rgsw: RGSW(m) is stored as single matrix of dimension (d_rgsw * 4,
|
||||
/// ring_size). The matrix has the following structure [RLWE'_A(-sm) ||
|
||||
/// RLWE'_B(-sm) || RLWE'_A(m) || RLWE'_B(m)]^T
|
||||
fn encrypt_rgsw<
|
||||
pub(crate) fn encrypt_rgsw<
|
||||
Mmut: MatrixMut + MatrixEntity,
|
||||
M: Matrix<MatElement = Mmut::MatElement> + Clone,
|
||||
S: Secret,
|
||||
@@ -283,7 +352,7 @@ fn encrypt_rgsw<
|
||||
assert!(m_eval.dimension() == (1, ring_size));
|
||||
|
||||
// RLWE(-sm), RLWE(-sm)
|
||||
let (rlwe_dash_nsm, rlwe_dash_m) = out_rgsw.split_at_row(d * 2);
|
||||
let (rlwe_dash_nsm, rlwe_dash_m) = out_rgsw.split_at_row_mut(d * 2);
|
||||
|
||||
let mut s_eval = Mmut::try_convert_from(s.values(), &q);
|
||||
ntt_op.forward(s_eval.get_row_mut(0).as_mut());
|
||||
@@ -364,7 +433,7 @@ fn encrypt_rgsw<
|
||||
/// - rgsw_in: RGSW(m') in evaluation domain
|
||||
/// - rlwe_in_decomposed: decomposed RLWE(m) in evaluation domain
|
||||
/// - rlwe_out: returned RLWE(mm') in evaluation domain
|
||||
fn rlwe_in_decomposed_evaluation_domain_mul_rgsw_rlwe_out_evaluation_domain<
|
||||
pub(crate) fn rlwe_in_decomposed_evaluation_domain_mul_rgsw_rlwe_out_evaluation_domain<
|
||||
Mmut: MatrixMut + MatrixEntity,
|
||||
M: Matrix<MatElement = Mmut::MatElement> + Clone,
|
||||
ModOp: VectorOps<Element = Mmut::MatElement>,
|
||||
@@ -381,7 +450,7 @@ fn rlwe_in_decomposed_evaluation_domain_mul_rgsw_rlwe_out_evaluation_domain<
|
||||
assert!(rlwe_in_decomposed_eval.dimension() == (2 * d_rgsw, ring_size));
|
||||
assert!(rlwe_out_eval.dimension() == (2, ring_size));
|
||||
|
||||
let (a_rlwe_out, b_rlwe_out) = rlwe_out_eval.split_at_row(1);
|
||||
let (a_rlwe_out, b_rlwe_out) = rlwe_out_eval.split_at_row_mut(1);
|
||||
|
||||
// a * RLWE'(-sm)
|
||||
let a_rlwe_dash_nsm = rgsw_in.iter_rows().take(d_rgsw);
|
||||
@@ -420,82 +489,165 @@ fn rlwe_in_decomposed_evaluation_domain_mul_rgsw_rlwe_out_evaluation_domain<
|
||||
});
|
||||
}
|
||||
|
||||
fn decompose_rlwe<
|
||||
M: Matrix + Clone,
|
||||
Mmut: MatrixMut<MatElement = M::MatElement> + MatrixEntity,
|
||||
D: Decomposer<Element = M::MatElement>,
|
||||
>(
|
||||
rlwe_in: &M,
|
||||
pub(crate) fn routine<M: Matrix + ?Sized, ModOp: VectorOps<Element = M::MatElement>>(
|
||||
write_to_row: &mut [M::MatElement],
|
||||
matrix_a: &[M::R],
|
||||
matrix_b: &[M::R],
|
||||
mod_op: &ModOp,
|
||||
) {
|
||||
izip!(matrix_a.iter(), matrix_b.iter()).for_each(|(a, b)| {
|
||||
mod_op.elwise_fma_mut(write_to_row, a.as_ref(), b.as_ref());
|
||||
});
|
||||
}
|
||||
|
||||
// pub(crate) fn decompose_rlwe<
|
||||
// M: Matrix + Clone,
|
||||
// Mmut: MatrixMut<MatElement = M::MatElement> + MatrixEntity,
|
||||
// D: Decomposer<Element = M::MatElement>,
|
||||
// >(
|
||||
// rlwe_in: &M,
|
||||
// decomposer: &D,
|
||||
// rlwe_in_decomposed: &mut Mmut,
|
||||
// ) where
|
||||
// M::MatElement: Copy,
|
||||
// <Mmut as Matrix>::R: RowMut,
|
||||
// {
|
||||
// let d_rgsw = decomposer.d();
|
||||
// let ring_size = rlwe_in.dimension().1;
|
||||
// assert!(rlwe_in_decomposed.dimension() == (2 * d_rgsw, ring_size));
|
||||
|
||||
// // Decompose rlwe_in
|
||||
// for ri in 0..ring_size {
|
||||
// // ai
|
||||
// let ai_decomposed = decomposer.decompose(rlwe_in.get(0, ri));
|
||||
// for j in 0..d_rgsw {
|
||||
// rlwe_in_decomposed.set(j, ri, ai_decomposed[j]);
|
||||
// }
|
||||
|
||||
// // bi
|
||||
// let bi_decomposed = decomposer.decompose(rlwe_in.get(1, ri));
|
||||
// for j in 0..d_rgsw {
|
||||
// rlwe_in_decomposed.set(j + d_rgsw, ri, bi_decomposed[j]);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
/// Decomposes ring polynomial r(X) into d polynomials using decomposer into
|
||||
/// output matrix decomp_r
|
||||
///
|
||||
/// Note that decomposition of r(X) requires decomposition of each of
|
||||
/// coefficients.
|
||||
///
|
||||
/// - decomp_r: must have dimensions d x ring_size. i^th decomposed polynomial
|
||||
/// will be stored at i^th row.
|
||||
pub(crate) fn decompose_r<M: Matrix + MatrixMut, D: Decomposer<Element = M::MatElement>>(
|
||||
r: &[M::MatElement],
|
||||
decomp_r: &mut [M::R],
|
||||
decomposer: &D,
|
||||
rlwe_in_decomposed: &mut Mmut,
|
||||
) where
|
||||
<M as Matrix>::R: RowMut,
|
||||
M::MatElement: Copy,
|
||||
<Mmut as Matrix>::R: RowMut,
|
||||
{
|
||||
let d_rgsw = decomposer.d();
|
||||
let ring_size = rlwe_in.dimension().1;
|
||||
assert!(rlwe_in_decomposed.dimension() == (2 * d_rgsw, ring_size));
|
||||
let ring_size = r.len();
|
||||
let d = decomposer.d();
|
||||
|
||||
// Decompose rlwe_in
|
||||
for ri in 0..ring_size {
|
||||
// ai
|
||||
let ai_decomposed = decomposer.decompose(rlwe_in.get(0, ri));
|
||||
for j in 0..d_rgsw {
|
||||
rlwe_in_decomposed.set(j, ri, ai_decomposed[j]);
|
||||
}
|
||||
|
||||
// bi
|
||||
let bi_decomposed = decomposer.decompose(rlwe_in.get(1, ri));
|
||||
for j in 0..d_rgsw {
|
||||
rlwe_in_decomposed.set(j + d_rgsw, ri, bi_decomposed[j]);
|
||||
let el_decomposed = decomposer.decompose(&r[ri]);
|
||||
for j in 0..d {
|
||||
decomp_r[j].as_mut()[ri] = el_decomposed[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns RLWE(m0m1) = RLWE(m0) x RGSW(m1)
|
||||
/// Returns RLWE(m0m1) = RLWE(m0) x RGSW(m1). Mutates rlwe_in inplace to equal
|
||||
/// RLWE(m0m1)
|
||||
///
|
||||
/// - rlwe_in: is RLWE(m0) with polynomials in coefficient domain
|
||||
/// - rgsw_in: is RGSW(m1) with polynomials in evaluation domain
|
||||
/// - rlwe_out: is output RLWE(m0m1) with polynomials in coefficient domain
|
||||
/// - rlwe_in_decomposed: is a matrix of dimension (d_rgsw * 2, ring_size) used
|
||||
/// as scratch space to store decomposed RLWE(m0)
|
||||
fn rlwe_by_rgsw<
|
||||
M: Matrix + Clone,
|
||||
Mmut: MatrixMut<MatElement = M::MatElement> + MatrixEntity,
|
||||
D: Decomposer<Element = M::MatElement>,
|
||||
ModOp: VectorOps<Element = M::MatElement>,
|
||||
NttOp: Ntt<Element = M::MatElement>,
|
||||
/// - scratch_matrix_d_ring: is a matrix of dimension (d_rgsw, ring_size) used
|
||||
/// as scratch space to store decomposed Ring elements temporarily
|
||||
pub(crate) fn rlwe_by_rgsw<
|
||||
Mmut: MatrixMut,
|
||||
MT: Matrix<MatElement = Mmut::MatElement> + MatrixMut<MatElement = Mmut::MatElement> + IsTrivial,
|
||||
D: Decomposer<Element = Mmut::MatElement>,
|
||||
ModOp: VectorOps<Element = Mmut::MatElement>,
|
||||
NttOp: Ntt<Element = Mmut::MatElement>,
|
||||
>(
|
||||
rlwe_in: &M,
|
||||
rgsw_in: &M,
|
||||
rlwe_out: &mut Mmut,
|
||||
rlwe_in_decomposed: &mut Mmut,
|
||||
rlwe_in: &mut MT,
|
||||
rgsw_in: &Mmut,
|
||||
scratch_matrix_dplus2_ring: &mut Mmut,
|
||||
decomposer: &D,
|
||||
ntt_op: &NttOp,
|
||||
mod_op: &ModOp,
|
||||
) where
|
||||
M::MatElement: Copy,
|
||||
Mmut::MatElement: Copy + Zero,
|
||||
<Mmut as Matrix>::R: RowMut,
|
||||
<MT as Matrix>::R: RowMut,
|
||||
{
|
||||
decompose_rlwe(rlwe_in, decomposer, rlwe_in_decomposed);
|
||||
|
||||
// transform rlwe_in decomposed to evaluation domain
|
||||
rlwe_in_decomposed
|
||||
.iter_rows_mut()
|
||||
.for_each(|r| ntt_op.forward(r.as_mut()));
|
||||
let d_rgsw = decomposer.d();
|
||||
assert!(scratch_matrix_dplus2_ring.dimension() == (d_rgsw + 2, rlwe_in.dimension().1));
|
||||
|
||||
// decomposed RLWE x RGSW
|
||||
rlwe_in_decomposed_evaluation_domain_mul_rgsw_rlwe_out_evaluation_domain(
|
||||
rgsw_in,
|
||||
rlwe_in_decomposed,
|
||||
rlwe_out,
|
||||
let (rlwe_dash_nsm, rlwe_dash_m) = rgsw_in.split_at_row(d_rgsw * 2);
|
||||
let (scratch_matrix_d_ring, scratch_rlwe_out) =
|
||||
scratch_matrix_dplus2_ring.split_at_row_mut(d_rgsw);
|
||||
scratch_rlwe_out[0].as_mut().fill(Mmut::MatElement::zero());
|
||||
scratch_rlwe_out[1].as_mut().fill(Mmut::MatElement::zero());
|
||||
// RLWE_in = a_in, b_in; RLWE_out = a_out, b_out
|
||||
if !rlwe_in.is_trivial() {
|
||||
// a_in = 0 when RLWE_in is trivial RLWE ciphertext
|
||||
// decomp<a_in>
|
||||
decompose_r::<Mmut, D>(rlwe_in.get_row_slice(0), scratch_matrix_d_ring, decomposer);
|
||||
scratch_matrix_d_ring
|
||||
.iter_mut()
|
||||
.for_each(|r| ntt_op.forward(r.as_mut()));
|
||||
// a_out += decomp<a_in> \cdot RLWE_A'(-sm)
|
||||
routine::<Mmut, ModOp>(
|
||||
scratch_rlwe_out[0].as_mut(),
|
||||
scratch_matrix_d_ring.as_ref(),
|
||||
&rlwe_dash_nsm[..d_rgsw],
|
||||
mod_op,
|
||||
);
|
||||
// b_out += decomp<a_in> \cdot RLWE_B'(-sm)
|
||||
routine::<Mmut, ModOp>(
|
||||
scratch_rlwe_out[1].as_mut(),
|
||||
scratch_matrix_d_ring.as_ref(),
|
||||
&rlwe_dash_nsm[d_rgsw..],
|
||||
mod_op,
|
||||
);
|
||||
}
|
||||
// decomp<b_in>
|
||||
decompose_r::<Mmut, D>(rlwe_in.get_row_slice(1), scratch_matrix_d_ring, decomposer);
|
||||
scratch_matrix_d_ring
|
||||
.iter_mut()
|
||||
.for_each(|r| ntt_op.forward(r.as_mut()));
|
||||
// a_out += decomp<b_in> \cdot RLWE_A'(m)
|
||||
routine::<Mmut, ModOp>(
|
||||
scratch_rlwe_out[0].as_mut(),
|
||||
scratch_matrix_d_ring.as_ref(),
|
||||
&rlwe_dash_m[..d_rgsw],
|
||||
mod_op,
|
||||
);
|
||||
// b_out += decomp<b_in> \cdot RLWE_B'(m)
|
||||
routine::<Mmut, ModOp>(
|
||||
scratch_rlwe_out[1].as_mut(),
|
||||
scratch_matrix_d_ring.as_ref(),
|
||||
&rlwe_dash_m[d_rgsw..],
|
||||
mod_op,
|
||||
);
|
||||
|
||||
// transform rlwe_out to coefficient domain
|
||||
rlwe_out
|
||||
.iter_rows_mut()
|
||||
scratch_rlwe_out
|
||||
.iter_mut()
|
||||
.for_each(|r| ntt_op.backward(r.as_mut()));
|
||||
|
||||
rlwe_in
|
||||
.get_row_mut(0)
|
||||
.copy_from_slice(scratch_rlwe_out[0].as_mut());
|
||||
rlwe_in
|
||||
.get_row_mut(1)
|
||||
.copy_from_slice(scratch_rlwe_out[1].as_mut());
|
||||
rlwe_in.set_not_trivial();
|
||||
}
|
||||
|
||||
/// Encrypt polynomial m(X) as RLWE ciphertext.
|
||||
@@ -503,7 +655,7 @@ fn rlwe_by_rgsw<
|
||||
/// - rlwe_out: returned RLWE ciphertext RLWE(m) in coefficient domain. RLWE
|
||||
/// ciphertext is a matirx with first row consiting polynomial `a` and the
|
||||
/// second rows consting polynomial `b`
|
||||
fn encrypt_rlwe<
|
||||
pub(crate) fn encrypt_rlwe<
|
||||
Mmut: Matrix + MatrixMut + Clone,
|
||||
ModOp: VectorOps<Element = Mmut::MatElement>,
|
||||
NttOp: Ntt<Element = Mmut::MatElement>,
|
||||
@@ -547,7 +699,7 @@ fn encrypt_rlwe<
|
||||
/// Decrypts degree 1 RLWE ciphertext RLWE(m) and returns m
|
||||
///
|
||||
/// - rlwe_ct: input degree 1 ciphertext RLWE(m).
|
||||
fn decrypt_rlwe<
|
||||
pub(crate) fn decrypt_rlwe<
|
||||
Mmut: MatrixMut + Clone,
|
||||
M: Matrix<MatElement = Mmut::MatElement>,
|
||||
ModOp: VectorOps<Element = Mmut::MatElement>,
|
||||
@@ -587,7 +739,7 @@ fn decrypt_rlwe<
|
||||
|
||||
// Measures noise in degree 1 RLWE ciphertext against encoded ideal message
|
||||
// encoded_m
|
||||
fn measure_noise<
|
||||
pub(crate) fn measure_noise<
|
||||
Mmut: MatrixMut + Matrix + MatrixEntity,
|
||||
ModOp: VectorOps<Element = Mmut::MatElement>,
|
||||
NttOp: Ntt<Element = Mmut::MatElement>,
|
||||
@@ -657,8 +809,9 @@ mod tests {
|
||||
decomposer::{gadget_vector, DefaultDecomposer},
|
||||
ntt::{self, Ntt, NttBackendU64},
|
||||
random::{DefaultSecureRng, RandomUniformDist},
|
||||
rgsw::measure_noise,
|
||||
rgsw::{measure_noise, RlweCiphertext},
|
||||
utils::{generate_prime, negacyclic_mul},
|
||||
Matrix,
|
||||
};
|
||||
|
||||
use super::{
|
||||
@@ -718,15 +871,14 @@ mod tests {
|
||||
&ntt_op,
|
||||
&mut rng,
|
||||
);
|
||||
let mut rlwe_in_ct = RlweCiphertext(rlwe_in_ct, false);
|
||||
|
||||
// RLWE(m0m1) = RLWE(m0) x RGSW(m1)
|
||||
let mut rlwe_out_ct = vec![vec![0u64; ring_size as usize]; 2];
|
||||
let mut scratch_space = vec![vec![0u64; ring_size as usize]; d_rgsw * 2];
|
||||
let mut scratch_space = vec![vec![0u64; ring_size as usize]; d_rgsw + 2];
|
||||
let decomposer = DefaultDecomposer::new(q, logb, d_rgsw);
|
||||
rlwe_by_rgsw(
|
||||
&rlwe_in_ct,
|
||||
&mut rlwe_in_ct,
|
||||
&rgsw_ct,
|
||||
&mut rlwe_out_ct,
|
||||
&mut scratch_space,
|
||||
&decomposer,
|
||||
&ntt_op,
|
||||
@@ -735,7 +887,7 @@ mod tests {
|
||||
|
||||
// Decrypt RLWE(m0m1)
|
||||
let mut encoded_m0m1_back = vec![vec![0u64; ring_size as usize]];
|
||||
decrypt_rlwe(&rlwe_out_ct, &s, &mut encoded_m0m1_back, &ntt_op, &mod_op);
|
||||
decrypt_rlwe(&rlwe_in_ct, &s, &mut encoded_m0m1_back, &ntt_op, &mod_op);
|
||||
let m0m1_back = encoded_m0m1_back[0]
|
||||
.iter()
|
||||
.map(|v| (((*v as f64 * p as f64) / (q as f64)).round() as u64) % p)
|
||||
@@ -797,14 +949,13 @@ mod tests {
|
||||
);
|
||||
|
||||
// Send RLWE_{s}(m) -> RLWE_{s}(m^k)
|
||||
let mut rlwe_m_k = vec![vec![0u64; ring_size as usize]; 2];
|
||||
let mut scratch_space = vec![vec![0u64; ring_size as usize]; d_rgsw];
|
||||
let mut rlwe_m = RlweCiphertext(rlwe_m, false);
|
||||
let mut scratch_space = vec![vec![0u64; ring_size as usize]; d_rgsw + 2];
|
||||
let (auto_map_index, auto_map_sign) = generate_auto_map(ring_size as usize, auto_k);
|
||||
let decomposer = DefaultDecomposer::new(q, logb, d_rgsw);
|
||||
galois_auto(
|
||||
&rlwe_m,
|
||||
&mut rlwe_m,
|
||||
&ksk_out,
|
||||
&mut rlwe_m_k,
|
||||
&mut scratch_space,
|
||||
&auto_map_index,
|
||||
&auto_map_sign,
|
||||
@@ -813,6 +964,8 @@ mod tests {
|
||||
&decomposer,
|
||||
);
|
||||
|
||||
let rlwe_m_k = rlwe_m;
|
||||
|
||||
// Decrypt RLWE_{s}(m^k) and check
|
||||
let mut encoded_m_k_back = vec![vec![0u64; ring_size as usize]];
|
||||
decrypt_rlwe(&rlwe_m_k, &s, &mut encoded_m_k_back, &ntt_op, &mod_op);
|
||||
@@ -834,13 +987,13 @@ mod tests {
|
||||
);
|
||||
|
||||
{
|
||||
let encoded_m_k = m_k
|
||||
.iter()
|
||||
.map(|v| ((*v as f64 * q as f64) / p as f64).round() as u64)
|
||||
.collect_vec();
|
||||
// let encoded_m_k = m_k
|
||||
// .iter()
|
||||
// .map(|v| ((*v as f64 * q as f64) / p as f64).round() as u64)
|
||||
// .collect_vec();
|
||||
|
||||
let noise = measure_noise(&rlwe_m_k, &vec![encoded_m_k], &ntt_op, &mod_op, &s);
|
||||
println!("Ksk noise: {noise}");
|
||||
// let noise = measure_noise(&rlwe_m_k, &vec![encoded_m_k], &ntt_op,
|
||||
// &mod_op, &s); println!("Ksk noise: {noise}");
|
||||
}
|
||||
|
||||
// FIXME(Jay): Galios autormophism will incur high error unless we fix in
|
||||
|
||||
21
src/utils.rs
21
src/utils.rs
@@ -189,3 +189,24 @@ impl TryConvertFrom<[i32]> for Vec<Vec<u64>> {
|
||||
vec![row0]
|
||||
}
|
||||
}
|
||||
|
||||
impl TryConvertFrom<[i32]> for Vec<u64> {
|
||||
type Parameters = u64;
|
||||
fn try_convert_from(value: &[i32], parameters: &Self::Parameters) -> Self {
|
||||
value
|
||||
.iter()
|
||||
.map(|v| {
|
||||
let is_neg = v.is_negative();
|
||||
let v_u64 = v.abs() as u64;
|
||||
|
||||
assert!(v_u64 < *parameters);
|
||||
|
||||
if is_neg {
|
||||
parameters - v_u64
|
||||
} else {
|
||||
v_u64
|
||||
}
|
||||
})
|
||||
.collect_vec()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user