test seeded RLWE auto key

This commit is contained in:
Janmajaya Mall
2024-05-01 17:22:49 +05:30
parent a6510189e8
commit c851a3b58f

View File

@@ -17,6 +17,77 @@ use crate::{
Matrix, MatrixEntity, MatrixMut, Row, RowEntity, RowMut, Secret, Matrix, MatrixEntity, MatrixMut, Row, RowEntity, RowMut, Secret,
}; };
pub struct SeededAutoKey<M, S>
where
M: Matrix,
{
data: M,
seed: S,
modulus: M::MatElement,
}
impl<M: Matrix + MatrixEntity, S> SeededAutoKey<M, S> {
fn from_raw(data: M, seed: S, modulus: M::MatElement) -> Self {
assert!(data.dimension().0 % 3 == 0);
SeededAutoKey {
data,
seed,
modulus,
}
}
fn empty(ring_size: usize, d_rgsw: usize, seed: S, modulus: M::MatElement) -> Self {
SeededAutoKey {
data: M::zeros(d_rgsw, ring_size),
seed,
modulus: modulus,
}
}
}
pub struct AutoKeyEvaluationDomain<M, R, N> {
data: M,
_phantom: PhantomData<(R, N)>,
}
impl<
M: MatrixMut + MatrixEntity,
R: RandomUniformDist<[M::MatElement], Parameters = M::MatElement> + NewWithSeed,
N: NttInit<Element = M::MatElement> + Ntt<Element = M::MatElement>,
> From<&SeededAutoKey<M, R::Seed>> for AutoKeyEvaluationDomain<M, R, N>
where
<M as Matrix>::R: RowMut,
M::MatElement: Copy,
R::Seed: Clone,
{
fn from(value: &SeededAutoKey<M, R::Seed>) -> Self {
let (d, ring_size) = value.data.dimension();
let mut data = M::zeros(2 * d, ring_size);
// sample RLWE'_A(-s(X^k))
let mut p_rng = R::new_with_seed(value.seed.clone());
data.iter_rows_mut().take(d).for_each(|r| {
RandomUniformDist::random_fill(&mut p_rng, &value.modulus, r.as_mut());
});
// copy over RLWE'_B(-s(X^k))
izip!(data.iter_rows_mut().skip(d), value.data.iter_rows()).for_each(|(to_r, from_r)| {
to_r.as_mut().copy_from_slice(from_r.as_ref());
});
// send RLWE'(-s(X^k)) polynomials to evaluation domain
let ntt_op = N::new(value.modulus, ring_size);
data.iter_rows_mut()
.for_each(|r| ntt_op.forward(r.as_mut()));
AutoKeyEvaluationDomain {
data,
_phantom: PhantomData,
}
}
}
pub struct SeededRgswCiphertext<M, S> pub struct SeededRgswCiphertext<M, S>
where where
M: Matrix, M: Matrix,
@@ -317,7 +388,7 @@ pub(crate) fn rlwe_ksk_gen<
+ NewWithSeed, + NewWithSeed,
>( >(
ksk_out: &mut Mmut, ksk_out: &mut Mmut,
mut neg_from_s: Mmut::R, neg_from_s: Mmut::R,
mut to_s: Mmut::R, mut to_s: Mmut::R,
gadget_vector: &[Mmut::MatElement], gadget_vector: &[Mmut::MatElement],
mod_op: &ModOp, mod_op: &ModOp,
@@ -843,46 +914,46 @@ pub(crate) fn decrypt_rlwe<
// Measures noise in degree 1 RLWE ciphertext against encoded ideal message // Measures noise in degree 1 RLWE ciphertext against encoded ideal message
// encoded_m // encoded_m
pub(crate) fn measure_noise< pub(crate) fn measure_noise<
Mmut: MatrixMut + Matrix + MatrixEntity, Mmut: MatrixMut + Matrix,
ModOp: VectorOps<Element = Mmut::MatElement>, ModOp: VectorOps<Element = Mmut::MatElement>,
NttOp: Ntt<Element = Mmut::MatElement>, NttOp: Ntt<Element = Mmut::MatElement>,
S: Secret, S,
>( >(
rlwe_ct: &Mmut, rlwe_ct: &Mmut,
encoded_m_ideal: &Mmut, encoded_m_ideal: &Mmut::R,
ntt_op: &NttOp, ntt_op: &NttOp,
mod_op: &ModOp, mod_op: &ModOp,
s: &S, s: &[S],
) -> f64 ) -> f64
where where
<Mmut as Matrix>::R: RowMut, <Mmut as Matrix>::R: RowMut,
Mmut: TryConvertFrom<[S::Element], Parameters = Mmut::MatElement>, Mmut::R: RowEntity + TryConvertFrom<[S], Parameters = Mmut::MatElement>,
Mmut::MatElement: PrimInt + ToPrimitive + Debug, Mmut::MatElement: PrimInt + ToPrimitive + Debug,
{ {
let ring_size = s.values().len(); let ring_size = s.len();
assert!(rlwe_ct.dimension() == (2, ring_size)); assert!(rlwe_ct.dimension() == (2, ring_size));
assert!(encoded_m_ideal.dimension() == (1, ring_size)); assert!(encoded_m_ideal.as_ref().len() == ring_size);
// -(s * a) // -(s * a)
let q = VectorOps::modulus(mod_op); let q = VectorOps::modulus(mod_op);
let mut s = Mmut::try_convert_from(s.values(), &q); let mut s = Mmut::R::try_convert_from(s, &q);
ntt_op.forward(s.get_row_mut(0)); ntt_op.forward(s.as_mut());
let mut a = Mmut::zeros(1, ring_size); let mut a = Mmut::R::zeros(ring_size);
a.get_row_mut(0).copy_from_slice(rlwe_ct.get_row_slice(0)); a.as_mut().copy_from_slice(rlwe_ct.get_row_slice(0));
ntt_op.forward(a.get_row_mut(0)); ntt_op.forward(a.as_mut());
mod_op.elwise_mul_mut(s.get_row_mut(0), a.get_row_slice(0)); mod_op.elwise_mul_mut(s.as_mut(), a.as_ref());
mod_op.elwise_neg_mut(s.get_row_mut(0)); mod_op.elwise_neg_mut(s.as_mut());
ntt_op.backward(s.get_row_mut(0)); ntt_op.backward(s.as_mut());
// m+e = b - s*a // m+e = b - s*a
let mut m_plus_e = s; let mut m_plus_e = s;
mod_op.elwise_add_mut(m_plus_e.get_row_mut(0), rlwe_ct.get_row_slice(1)); mod_op.elwise_add_mut(m_plus_e.as_mut(), rlwe_ct.get_row_slice(1));
// difference // difference
mod_op.elwise_sub_mut(m_plus_e.get_row_mut(0), encoded_m_ideal.get_row_slice(0)); mod_op.elwise_sub_mut(m_plus_e.as_mut(), encoded_m_ideal.as_ref());
let mut max_diff_bits = f64::MIN; let mut max_diff_bits = f64::MIN;
m_plus_e.get_row_slice(0).iter().for_each(|v| { m_plus_e.as_ref().iter().for_each(|v| {
let mut v = *v; let mut v = *v;
if v >= (q >> 1) { if v >= (q >> 1) {
@@ -913,8 +984,8 @@ mod tests {
ntt::{self, Ntt, NttBackendU64, NttInit}, ntt::{self, Ntt, NttBackendU64, NttInit},
random::{DefaultSecureRng, RandomUniformDist}, random::{DefaultSecureRng, RandomUniformDist},
rgsw::{ rgsw::{
measure_noise, RgswCiphertextEvaluationDomain, RlweCiphertext, SeededRgswCiphertext, measure_noise, AutoKeyEvaluationDomain, RgswCiphertextEvaluationDomain, RlweCiphertext,
SeededRlweCiphertext, SeededAutoKey, SeededRgswCiphertext, SeededRlweCiphertext,
}, },
utils::{generate_prime, negacyclic_mul}, utils::{generate_prime, negacyclic_mul},
Matrix, Secret, Matrix, Secret,
@@ -1083,113 +1154,122 @@ mod tests {
); );
} }
// #[test] #[test]
// fn galois_auto_works() { fn galois_auto_works() {
// let logq = 50; let logq = 50;
// let ring_size = 1 << 5; let ring_size = 1 << 4;
// let q = generate_prime(logq, 2 * ring_size, 1u64 << logq).unwrap(); let q = generate_prime(logq, 2 * ring_size, 1u64 << logq).unwrap();
// let logp = 3; let logp = 3;
// let p = 1u64 << logp; let p = 1u64 << logp;
// let d_rgsw = 10; let d_rgsw = 10;
// let logb = 5; let logb = 5;
// let mut rng = DefaultSecureRng::new(); let mut rng = DefaultSecureRng::new();
// let s = RlweSecret::random((ring_size >> 1) as usize, ring_size as let s = RlweSecret::random((ring_size >> 1) as usize, ring_size as usize);
// usize);
// let mut m = vec![0u64; ring_size as usize]; let mut m = vec![0u64; ring_size as usize];
// RandomUniformDist::random_fill(&mut rng, &p, m.as_mut_slice()); RandomUniformDist::random_fill(&mut rng, &p, m.as_mut_slice());
// let encoded_m = m let encoded_m = m
// .iter() .iter()
// .map(|v| (((*v as f64 * q as f64) / (p as f64)).round() as u64)) .map(|v| (((*v as f64 * q as f64) / (p as f64)).round() as u64))
// .collect_vec(); .collect_vec();
// let ntt_op = NttBackendU64::new(q, ring_size as usize); let ntt_op = NttBackendU64::new(q, ring_size as usize);
// let mod_op = ModularOpsU64::new(q); let mod_op = ModularOpsU64::new(q);
// // RLWE_{s}(m) // RLWE_{s}(m)
// let mut rlwe_m = vec![vec![0u64; ring_size as usize]; 2]; let mut seed_rlwe = [0u8; 32];
// secret_key_encrypt_rlwe( rng.fill_bytes(&mut seed_rlwe);
// &vec![encoded_m.clone()], let mut seeded_rlwe_m = SeededRlweCiphertext::empty(ring_size as usize, seed_rlwe, q);
// &mut rlwe_m, secret_key_encrypt_rlwe(
// &s, &encoded_m,
// &mod_op, &mut seeded_rlwe_m.data,
// &ntt_op, s.values(),
// &mut rng, &mod_op,
// ); &ntt_op,
seeded_rlwe_m.seed,
&mut rng,
);
let mut rlwe_m = RlweCiphertext::<Vec<Vec<u64>>, DefaultSecureRng>::from(&seeded_rlwe_m);
// let auto_k = -5; let auto_k = -5;
// // Generate galois key to key switch from s^k to s // Generate galois key to key switch from s^k to s
// let mut ksk_out = vec![vec![0u64; ring_size as usize]; d_rgsw * 2]; let mut seed_auto = [0u8; 32];
// let gadget_vector = gadget_vector(logq, logb, d_rgsw); rng.fill_bytes(&mut seed_auto);
// galois_key_gen( let mut seeded_auto_key = SeededAutoKey::empty(ring_size as usize, d_rgsw, seed_auto, q);
// &mut ksk_out, let gadget_vector = gadget_vector(logq, logb, d_rgsw);
// &s, galois_key_gen(
// auto_k, &mut seeded_auto_key.data,
// &gadget_vector, s.values(),
// &mod_op, auto_k,
// &ntt_op, &gadget_vector,
// &mut rng, &mod_op,
// ); &ntt_op,
seeded_auto_key.seed,
&mut rng,
);
let auto_key =
AutoKeyEvaluationDomain::<Vec<Vec<u64>>, DefaultSecureRng, NttBackendU64>::from(
&seeded_auto_key,
);
// // Send RLWE_{s}(m) -> RLWE_{s}(m^k) // Send RLWE_{s}(m) -> RLWE_{s}(m^k)
// let mut rlwe_m = RlweCiphertext(rlwe_m, false); 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 + let (auto_map_index, auto_map_sign) = generate_auto_map(ring_size as usize, auto_k);
// 2]; let (auto_map_index, auto_map_sign) = let decomposer = DefaultDecomposer::new(q, logb, d_rgsw);
// generate_auto_map(ring_size as usize, auto_k); let decomposer = galois_auto(
// DefaultDecomposer::new(q, logb, d_rgsw); galois_auto( &mut rlwe_m,
// &mut rlwe_m, &auto_key.data,
// &ksk_out, &mut scratch_space,
// &mut scratch_space, &auto_map_index,
// &auto_map_index, &auto_map_sign,
// &auto_map_sign, &mod_op,
// &mod_op, &ntt_op,
// &ntt_op, &decomposer,
// &decomposer, );
// );
// let rlwe_m_k = rlwe_m; let rlwe_m_k = rlwe_m;
// // Decrypt RLWE_{s}(m^k) and check // Decrypt RLWE_{s}(m^k) and check
// let mut encoded_m_k_back = vec![vec![0u64; ring_size as usize]]; let mut encoded_m_k_back = vec![0u64; ring_size as usize];
// decrypt_rlwe( decrypt_rlwe(
// &rlwe_m_k, &rlwe_m_k,
// s.values(), s.values(),
// &mut encoded_m_k_back, &mut encoded_m_k_back,
// &ntt_op, &ntt_op,
// &mod_op, &mod_op,
// ); );
// let m_k_back = encoded_m_k_back[0] let m_k_back = encoded_m_k_back
// .iter() .iter()
// .map(|v| (((*v as f64 * p as f64) / q as f64).round() as u64) % .map(|v| (((*v as f64 * p as f64) / q as f64).round() as u64) % p)
// p) .collect_vec(); .collect_vec();
// let mut m_k = vec![0u64; ring_size as usize]; let mut m_k = vec![0u64; ring_size as usize];
// // Send \delta m -> \delta m^k // Send \delta m -> \delta m^k
// izip!(m.iter(), auto_map_index.iter(), izip!(m.iter(), auto_map_index.iter(), auto_map_sign.iter()).for_each(
// auto_map_sign.iter()).for_each( |(v, to_index, sign)| { |(v, to_index, sign)| {
// if !*sign { if !*sign {
// m_k[*to_index] = (p - *v) % p; m_k[*to_index] = (p - *v) % p;
// } else { } else {
// m_k[*to_index] = *v; m_k[*to_index] = *v;
// } }
// }, },
// ); );
// { {
// // let encoded_m_k = m_k let encoded_m_k = m_k
// // .iter() .iter()
// // .map(|v| ((*v as f64 * q as f64) / p as f64).round() as .map(|v| ((*v as f64 * q as f64) / p as f64).round() as u64)
// u64) // .collect_vec(); .collect_vec();
// // let noise = measure_noise(&rlwe_m_k, &vec![encoded_m_k], let noise = measure_noise(&rlwe_m_k, &encoded_m_k, &ntt_op, &mod_op, s.values());
// &ntt_op, // &mod_op, &s); println!("Ksk noise: {noise}"); println!("Ksk noise: {noise}");
// } }
// // FIXME(Jay): Galios autormophism will incur high error unless we // FIXME(Jay): Galios autormophism will incur high error unless we fix in
// fix in // accurate decomoposition of Decomposer when q is prime // accurate decomoposition of Decomposer when q is prime
// assert_eq!(m_k_back, m_k); assert_eq!(m_k_back, m_k);
// // dbg!(m_k_back, m_k, q); // dbg!(m_k_back, m_k, q);
// } }
} }