Browse Source

test seeded RLWE auto key

par-agg-key-shares
Janmajaya Mall 11 months ago
parent
commit
c851a3b58f
1 changed files with 210 additions and 130 deletions
  1. +210
    -130
      src/rgsw.rs

+ 210
- 130
src/rgsw.rs

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

Loading…
Cancel
Save