Browse Source

fix test vector to q/8

par-agg-key-shares
Janmajaya Mall 11 months ago
parent
commit
9257ca25ee
6 changed files with 499 additions and 346 deletions
  1. +280
    -176
      src/bool/evaluator.rs
  2. +9
    -10
      src/bool/parameters.rs
  3. +4
    -4
      src/decomposer.rs
  4. +2
    -4
      src/lwe.rs
  5. +2
    -2
      src/random.rs
  6. +202
    -150
      src/rgsw.rs

+ 280
- 176
src/bool/evaluator.rs

@ -2,9 +2,7 @@ use std::{
cell::RefCell, cell::RefCell,
collections::HashMap, collections::HashMap,
fmt::{Debug, Display}, fmt::{Debug, Display},
hash::Hash,
marker::PhantomData, marker::PhantomData,
thread::panicking,
}; };
use itertools::{izip, partition, Itertools}; use itertools::{izip, partition, Itertools};
@ -45,6 +43,7 @@ trait PbsKey {
/// LWE ksk to key switch from RLWE secret to LWE secret /// LWE ksk to key switch from RLWE secret to LWE secret
fn lwe_ksk(&self) -> &Self::M; fn lwe_ksk(&self) -> &Self::M;
} }
trait PbsParameters { trait PbsParameters {
type Element; type Element;
type D: Decomposer<Element = Self::Element>; type D: Decomposer<Element = Self::Element>;
@ -570,6 +569,7 @@ where
embedding_factor: usize, embedding_factor: usize,
nand_test_vec: M::R, nand_test_vec: M::R,
rlweq_by8: M::MatElement, rlweq_by8: M::MatElement,
rlwe_qby4: M::MatElement,
rlwe_auto_maps: Vec<(Vec<usize>, Vec<bool>)>, rlwe_auto_maps: Vec<(Vec<usize>, Vec<bool>)>,
_phantom: PhantomData<M>, _phantom: PhantomData<M>,
} }
@ -582,7 +582,7 @@ where
+ VectorOps<Element = M::MatElement>, + VectorOps<Element = M::MatElement>,
M::MatElement: PrimInt + Debug + Display + NumInfo + FromPrimitive + WrappingSub, M::MatElement: PrimInt + Debug + Display + NumInfo + FromPrimitive + WrappingSub,
M: MatrixEntity + MatrixMut, M: MatrixEntity + MatrixMut,
M::R: TryConvertFrom<[i32], Parameters = M::MatElement> + RowEntity,
M::R: TryConvertFrom<[i32], Parameters = M::MatElement> + RowEntity + Debug,
M: TryConvertFrom<[i32], Parameters = M::MatElement>, M: TryConvertFrom<[i32], Parameters = M::MatElement>,
<M as Matrix>::R: RowMut, <M as Matrix>::R: RowMut,
DefaultSecureRng: RandomGaussianDist<[M::MatElement], Parameters = M::MatElement> DefaultSecureRng: RandomGaussianDist<[M::MatElement], Parameters = M::MatElement>
@ -630,7 +630,6 @@ where
let q = parameters.br_q; let q = parameters.br_q;
let qby2 = q >> 1; let qby2 = q >> 1;
let qby8 = q >> 3; let qby8 = q >> 3;
let qby16 = q >> 4;
let mut nand_test_vec = M::R::zeros(qby2); let mut nand_test_vec = M::R::zeros(qby2);
// Q/8 (Q: rlwe_q) // Q/8 (Q: rlwe_q)
let rlwe_qby8 = let rlwe_qby8 =
@ -646,14 +645,14 @@ where
nand_test_vec.as_mut()[i] = false_m_el; nand_test_vec.as_mut()[i] = false_m_el;
} }
} }
// Rotate and negate by q/16
let mut tmp = M::R::zeros(qby2);
tmp.as_mut()[..qby2 - qby16].copy_from_slice(&nand_test_vec.as_ref()[qby16..]);
tmp.as_mut()[qby2 - qby16..].copy_from_slice(&nand_test_vec.as_ref()[..qby16]);
tmp.as_mut()[qby2 - qby16..].iter_mut().for_each(|v| {
*v = parameters.rlwe_q - *v;
});
let nand_test_vec = tmp;
// // Rotate and negate by q/8
// let mut tmp = M::R::zeros(qby2);
// tmp.as_mut()[..qby2 - qby8].copy_from_slice(&nand_test_vec.as_ref()[qby8..]);
// tmp.as_mut()[qby2 - qby8..].copy_from_slice(&nand_test_vec.as_ref()[..qby8]);
// tmp.as_mut()[qby2 - qby8..].iter_mut().for_each(|v| {
// *v = parameters.rlwe_q - *v;
// });
// let nand_test_vec = tmp;
// v(X) -> v(X^{-g}) // v(X) -> v(X^{-g})
let (auto_map_index, auto_map_sign) = generate_auto_map(qby2, -(g as isize)); let (auto_map_index, auto_map_sign) = generate_auto_map(qby2, -(g as isize));
@ -680,6 +679,9 @@ where
rlwe_auto_maps.push(generate_auto_map(ring_size, i)) rlwe_auto_maps.push(generate_auto_map(ring_size, i))
} }
let rlwe_qby4 =
M::MatElement::from_f64((parameters.rlwe_q.to_f64().unwrap() / 4.0).round()).unwrap();
BoolEvaluator { BoolEvaluator {
parameters: parameters, parameters: parameters,
decomposer_lwe, decomposer_lwe,
@ -691,6 +693,7 @@ where
rlwe_nttop, rlwe_nttop,
nand_test_vec: nand_test_vec_autog, nand_test_vec: nand_test_vec_autog,
rlweq_by8: rlwe_qby8, rlweq_by8: rlwe_qby8,
rlwe_qby4: rlwe_qby4,
rlwe_auto_maps, rlwe_auto_maps,
_phantom: PhantomData, _phantom: PhantomData,
@ -1116,8 +1119,11 @@ where
// ); // );
// let c1_noise = // let c1_noise =
// measure_noise_lwe(c1, ck.sk_rlwe.values(), &self.rlwe_modop, // measure_noise_lwe(c1, ck.sk_rlwe.values(), &self.rlwe_modop,
// &(self.rlweq_by8)); println!("c0 noise: {c0_noise}; c1 noise:
// {c1_noise}"); });
// &(self.rlweq_by8)); println!(
// "c0 noise: {c0_noise}; c1 noise:
// {c1_noise}"
// );
// });
let mut c_out = M::R::zeros(c0.as_ref().len()); let mut c_out = M::R::zeros(c0.as_ref().len());
let modop = &self.rlwe_modop; let modop = &self.rlwe_modop;
@ -1130,7 +1136,7 @@ where
*o = modop.add(i0, i1); *o = modop.add(i0, i1);
}); });
// +Q/8 // +Q/8
c_out.as_mut()[0] = modop.add(&c_out.as_ref()[0], &self.rlweq_by8);
c_out.as_mut()[0] = modop.add(&c_out.as_ref()[0], &self.rlwe_qby4);
// ClientKey::with_local(|ck| { // ClientKey::with_local(|ck| {
// let noise = measure_noise_lwe( // let noise = measure_noise_lwe(
@ -1364,6 +1370,15 @@ fn pbs<
let br_qf64 = br_q.to_f64().unwrap(); let br_qf64 = br_q.to_f64().unwrap();
let rlwe_n = parameters.rlwe_n(); let rlwe_n = parameters.rlwe_n();
PBSTracer::with_local_mut(|t| {
let out = lwe_in
.as_ref()
.iter()
.map(|v| v.to_u64().unwrap())
.collect_vec();
t.ct_rlwe_q_mod = out;
});
// moddown Q -> Q_ks // moddown Q -> Q_ks
lwe_in.as_mut().iter_mut().for_each(|v| { lwe_in.as_mut().iter_mut().for_each(|v| {
*v = *v =
@ -1590,6 +1605,7 @@ struct PBSTracer
where where
M: Matrix + Default, M: Matrix + Default,
{ {
pub(crate) ct_rlwe_q_mod: M::R,
pub(crate) ct_lwe_q_mod: M::R, pub(crate) ct_lwe_q_mod: M::R,
pub(crate) ct_lwe_q_mod_after_ksk: M::R, pub(crate) ct_lwe_q_mod_after_ksk: M::R,
pub(crate) ct_br_q_mod: Vec<u64>, pub(crate) ct_br_q_mod: Vec<u64>,
@ -1600,21 +1616,26 @@ impl PBSTracer>> {
assert!(parameters.rlwe_n == sk_rlwe.len()); assert!(parameters.rlwe_n == sk_rlwe.len());
assert!(parameters.lwe_n == sk_lwe.len()); assert!(parameters.lwe_n == sk_lwe.len());
let modop_rlweq = ModularOpsU64::new(parameters.rlwe_q as u64);
// noise after mod down Q -> Q_ks
let m_back0 = decrypt_lwe(&self.ct_rlwe_q_mod, sk_rlwe, &modop_rlweq);
let modop_lweq = ModularOpsU64::new(parameters.lwe_q as u64); let modop_lweq = ModularOpsU64::new(parameters.lwe_q as u64);
// noise after mod down Q -> Q_ks // noise after mod down Q -> Q_ks
let m_back0 = decrypt_lwe(&self.ct_lwe_q_mod, sk_rlwe, &modop_lweq);
let m_back1 = decrypt_lwe(&self.ct_lwe_q_mod, sk_rlwe, &modop_lweq);
// noise after key switch from RLWE -> LWE // noise after key switch from RLWE -> LWE
let m_back1 = decrypt_lwe(&self.ct_lwe_q_mod_after_ksk, sk_lwe, &modop_lweq);
let m_back2 = decrypt_lwe(&self.ct_lwe_q_mod_after_ksk, sk_lwe, &modop_lweq);
// noise after mod down odd from Q_ks -> q // noise after mod down odd from Q_ks -> q
let modop_br_q = ModularOpsU64::new(parameters.br_q as u64); let modop_br_q = ModularOpsU64::new(parameters.br_q as u64);
let m_back2 = decrypt_lwe(&self.ct_br_q_mod, sk_lwe, &modop_br_q);
let m_back3 = decrypt_lwe(&self.ct_br_q_mod, sk_lwe, &modop_br_q);
println!( println!(
" "
M after mod down Q -> Q_ks: {m_back0},
M after key switch from RLWE -> LWE: {m_back1},
M after mod dwon Q_ks -> q: {m_back2}
M initial mod Q: {m_back0},
M after mod down Q -> Q_ks: {m_back1},
M after key switch from RLWE -> LWE: {m_back2},
M after mod dwon Q_ks -> q: {m_back3}
" "
); );
} }
@ -1639,6 +1660,7 @@ impl WithLocal for PBSTracer>> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use rand::{thread_rng, Rng}; use rand::{thread_rng, Rng};
use rand_distr::Uniform;
use crate::{ use crate::{
backend::ModularOpsU64, backend::ModularOpsU64,
@ -1708,6 +1730,7 @@ mod tests {
let mut m1 = true; let mut m1 = true;
let mut ct0 = bool_evaluator.sk_encrypt(m0, &client_key); let mut ct0 = bool_evaluator.sk_encrypt(m0, &client_key);
let mut ct1 = bool_evaluator.sk_encrypt(m1, &client_key); let mut ct1 = bool_evaluator.sk_encrypt(m1, &client_key);
for _ in 0..1000 { for _ in 0..1000 {
let ct_back = bool_evaluator.nand( let ct_back = bool_evaluator.nand(
&ct0, &ct0,
@ -1721,6 +1744,45 @@ mod tests {
// Trace and measure PBS noise // Trace and measure PBS noise
{ {
let noise0 = {
let ideal = if m0 {
bool_evaluator.rlweq_by8
} else {
bool_evaluator.rlwe_q() - bool_evaluator.rlweq_by8
};
let n = measure_noise_lwe(
&ct0,
client_key.sk_rlwe.values(),
&bool_evaluator.rlwe_modop,
&ideal,
);
let v = decrypt_lwe(
&ct0,
client_key.sk_rlwe.values(),
&bool_evaluator.rlwe_modop,
);
(n, v)
};
let noise1 = {
let ideal = if m1 {
bool_evaluator.rlweq_by8
} else {
bool_evaluator.rlwe_q() - bool_evaluator.rlweq_by8
};
let n = measure_noise_lwe(
&ct1,
client_key.sk_rlwe.values(),
&bool_evaluator.rlwe_modop,
&ideal,
);
let v = decrypt_lwe(
&ct1,
client_key.sk_rlwe.values(),
&bool_evaluator.rlwe_modop,
);
(n, v)
};
// Trace PBS // Trace PBS
PBSTracer::with_local(|t| { PBSTracer::with_local(|t| {
t.trace( t.trace(
@ -1731,21 +1793,32 @@ mod tests {
}); });
// Calculate noise in ciphertext post PBS // Calculate noise in ciphertext post PBS
let ideal = if m_out {
bool_evaluator.rlweq_by8
} else {
bool_evaluator.rlwe_q() - bool_evaluator.rlweq_by8
let noise_out = {
let ideal = if m_out {
bool_evaluator.rlweq_by8
} else {
bool_evaluator.rlwe_q() - bool_evaluator.rlweq_by8
};
let n = measure_noise_lwe(
&ct_back,
client_key.sk_rlwe.values(),
&bool_evaluator.rlwe_modop,
&ideal,
);
let v = decrypt_lwe(
&ct_back,
client_key.sk_rlwe.values(),
&bool_evaluator.rlwe_modop,
);
(n, v)
}; };
let noise = measure_noise_lwe(
&ct_back,
client_key.sk_rlwe.values(),
&bool_evaluator.rlwe_modop,
&ideal,
dbg!(m0, m1, m_out);
println!(
"ct0 (noise, message): {:?} \n ct1 (noise, message): {:?} \n PBS (noise, message): {:?}", noise0, noise1, noise_out
); );
println!("PBS noise: {noise}");
} }
let m_back = bool_evaluator.sk_decrypt(&ct_back, &client_key); let m_back = bool_evaluator.sk_decrypt(&ct_back, &client_key);
assert_eq!(m_out, m_back);
assert!(m_out == m_back, "Expected {m_out}, got {m_back}");
println!("----------"); println!("----------");
m1 = m0; m1 = m0;
@ -1761,7 +1834,7 @@ mod tests {
let bool_evaluator = let bool_evaluator =
BoolEvaluator::<Vec<Vec<u64>>, u64, NttBackendU64, ModularOpsU64>::new(MP_BOOL_PARAMS); BoolEvaluator::<Vec<Vec<u64>>, u64, NttBackendU64, ModularOpsU64>::new(MP_BOOL_PARAMS);
let no_of_parties = 5;
let no_of_parties = 500;
let parties = (0..no_of_parties) let parties = (0..no_of_parties)
.map(|_| bool_evaluator.client_key()) .map(|_| bool_evaluator.client_key())
.collect_vec(); .collect_vec();
@ -1773,6 +1846,8 @@ mod tests {
}); });
}); });
println!("{:?}", &ideal_rlwe_sk);
let mut m = true; let mut m = true;
for i in 0..100 { for i in 0..100 {
let pk_cr_seed = [0u8; 32]; let pk_cr_seed = [0u8; 32];
@ -1814,18 +1889,68 @@ mod tests {
} }
} }
#[test]
fn ms() {
let logbig_q = 50;
let logsmall_q = 20;
let big_q = 1 << logbig_q;
let small_q = 1 << logsmall_q;
let lwe_n = 493;
let no_of_parties = 10;
let parties_lwe_sk = (0..no_of_parties)
.map(|_| LweSecret::random(lwe_n >> 1, lwe_n))
.collect_vec();
// Ideal secrets
let mut ideal_lwe_sk = vec![0i32; lwe_n];
parties_lwe_sk.iter().for_each(|k| {
izip!(ideal_lwe_sk.iter_mut(), k.values()).for_each(|(ideal_i, s_i)| {
*ideal_i = *ideal_i + s_i;
});
});
let mut rng = DefaultSecureRng::new();
let logp = 3;
let modop_bigq = ModularOpsU64::new(big_q);
let modop_smallq = ModularOpsU64::new(small_q);
for i in 0..100 {
let m = thread_rng().sample(Uniform::new(0u64, (1u64 << logp)));
let bigq_m = m << (logbig_q - logp);
let smallq_m = m << (logsmall_q - logp);
// encrypt
let mut lwe_ct = vec![0u64; lwe_n + 1];
encrypt_lwe(&mut lwe_ct, &bigq_m, &ideal_lwe_sk, &modop_bigq, &mut rng);
let noise = measure_noise_lwe(&lwe_ct, &ideal_lwe_sk, &modop_bigq, &bigq_m);
println!("Noise Before: {noise}");
// mod switch
let lwe_ct_ms = lwe_ct
.iter()
.map(|v| (((*v as f64) * small_q as f64) / (big_q as f64)).round() as u64)
.collect_vec();
let noise = measure_noise_lwe(&lwe_ct_ms, &ideal_lwe_sk, &modop_smallq, &smallq_m);
println!("Noise After: {noise}");
}
}
#[test] #[test]
fn multi_party_lwe_keyswitch() { fn multi_party_lwe_keyswitch() {
let lwe_logq = 18; let lwe_logq = 18;
let lwe_q = 1 << lwe_logq; let lwe_q = 1 << lwe_logq;
let d_lwe = 4;
let logb_lwe = 4;
let d_lwe = 1;
let logb_lwe = 6;
let lwe_gadgect_vec = gadget_vector(lwe_logq, logb_lwe, d_lwe); let lwe_gadgect_vec = gadget_vector(lwe_logq, logb_lwe, d_lwe);
let lweq_modop = ModularOpsU64::new(lwe_q); let lweq_modop = ModularOpsU64::new(lwe_q);
let logp = 2; let logp = 2;
let from_lwe_n = 2048; let from_lwe_n = 2048;
let to_lwe_n = 583;
let to_lwe_n = 500;
let no_of_parties = 10; let no_of_parties = 10;
let parties_from_lwe_sk = (0..no_of_parties) let parties_from_lwe_sk = (0..no_of_parties)
@ -1835,12 +1960,26 @@ mod tests {
.map(|_| LweSecret::random(to_lwe_n >> 1, to_lwe_n)) .map(|_| LweSecret::random(to_lwe_n >> 1, to_lwe_n))
.collect_vec(); .collect_vec();
// Ideal secrets
let mut ideal_from_lwe_sk = vec![0i32; from_lwe_n];
parties_from_lwe_sk.iter().for_each(|k| {
izip!(ideal_from_lwe_sk.iter_mut(), k.values()).for_each(|(ideal_i, s_i)| {
*ideal_i = *ideal_i + s_i;
});
});
let mut ideal_to_lwe_sk = vec![0i32; to_lwe_n];
parties_to_lwe_sk.iter().for_each(|k| {
izip!(ideal_to_lwe_sk.iter_mut(), k.values()).for_each(|(ideal_i, s_i)| {
*ideal_i = *ideal_i + s_i;
});
});
// Generate Lwe KSK share // Generate Lwe KSK share
let mut rng = DefaultSecureRng::new(); let mut rng = DefaultSecureRng::new();
let mut ksk_seed = [0u8; 32]; let mut ksk_seed = [0u8; 32];
rng.fill_bytes(&mut ksk_seed); rng.fill_bytes(&mut ksk_seed);
let lwe_ksk_shares =
izip!(parties_from_lwe_sk.iter(), parties_to_lwe_sk.iter()).map(|(from_sk, to_sk)| {
let lwe_ksk_shares = izip!(parties_from_lwe_sk.iter(), parties_to_lwe_sk.iter())
.map(|(from_sk, to_sk)| {
let mut ksk_out = vec![0u64; from_lwe_n * d_lwe]; let mut ksk_out = vec![0u64; from_lwe_n * d_lwe];
let mut p_rng = DefaultSecureRng::new_seeded(ksk_seed); let mut p_rng = DefaultSecureRng::new_seeded(ksk_seed);
lwe_ksk_keygen( lwe_ksk_keygen(
@ -1853,11 +1992,12 @@ mod tests {
&mut rng, &mut rng,
); );
ksk_out ksk_out
});
})
.collect_vec();
// Create collective LWE ksk // Create collective LWE ksk
let mut sum_partb = vec![0u64; d_lwe * from_lwe_n]; let mut sum_partb = vec![0u64; d_lwe * from_lwe_n];
lwe_ksk_shares.for_each(|share| {
lwe_ksk_shares.iter().for_each(|share| {
lweq_modop.elwise_add_mut(sum_partb.as_mut_slice(), share.as_slice()) lweq_modop.elwise_add_mut(sum_partb.as_mut_slice(), share.as_slice())
}); });
let mut lwe_ksk = vec![vec![0u64; to_lwe_n + 1]; d_lwe * from_lwe_n]; let mut lwe_ksk = vec![vec![0u64; to_lwe_n + 1]; d_lwe * from_lwe_n];
@ -1867,71 +2007,34 @@ mod tests {
lwe_i[0] = *part_bi; lwe_i[0] = *part_bi;
}); });
// Collective pk
// let collective_pk = _collecitve_public_key_gen(
// lwe_q,
// &parties_from_lwe_sk
// .iter()
// .map(|s| RlweSecret {
// values: s.values.clone(),
// })
// .collect_vec(),
// );
// // Encrypt m as LWE ciphertext
// let m = 1;
// let lwe_ct = {
// let nttop = NttBackendU64::new(lwe_q, from_lwe_n);
// let modop = ModularOpsU64::new(lwe_q);
// let mut rlwe_out = vec![vec![0u64]; from_lwe_n];
// let mut m_vec = vec![0u64; from_lwe_n];
// m_vec[0] = m;
// public_key_encrypt_rlwe(
// &mut rlwe_out,
// &collective_pk,
// &m_vec,
// &modop,
// &nttop,
// &mut rng,
// );
// let mut lwe_ct = vec![0u64; from_lwe_n + 1];
// sample_extract(&mut lwe_ct, &rlwe_out, &modop, 0);
// lwe_ct
// };
// Encrypt m
let m = 1;
let mut ideal_from_lwe_sk = vec![0i32; from_lwe_n];
parties_from_lwe_sk.iter().for_each(|k| {
izip!(ideal_from_lwe_sk.iter_mut(), k.values()).for_each(|(ideal_i, s_i)| {
*ideal_i = *ideal_i + s_i;
});
});
let mut lwe_ct = vec![0u64; from_lwe_n + 1];
encrypt_lwe(&mut lwe_ct, &m, &ideal_from_lwe_sk, &lweq_modop, &mut rng);
for i in 0..128 {
println!("############## ITERATION {i} ##############");
// Key switch
let lwe_ct_key_switched = {
let mut lwe_ct_key_switched = vec![0u64; to_lwe_n + 1];
let decomposer = DefaultDecomposer::new(lwe_q, logb_lwe, d_lwe);
lwe_key_switch(
&mut lwe_ct_key_switched,
&lwe_ct,
&lwe_ksk,
&lweq_modop,
&decomposer,
);
lwe_ct_key_switched
};
// Encrypt m
let m = 1;
let mut lwe_ct = vec![0u64; from_lwe_n + 1];
encrypt_lwe(&mut lwe_ct, &m, &ideal_from_lwe_sk, &lweq_modop, &mut rng);
let noise = measure_noise_lwe(&lwe_ct, &ideal_from_lwe_sk, &lweq_modop, &m);
println!("Noise before key switch: {noise}");
// Key switch
let lwe_ct_key_switched = {
let mut lwe_ct_key_switched = vec![0u64; to_lwe_n + 1];
let decomposer = DefaultDecomposer::new(lwe_q, logb_lwe, d_lwe);
lwe_key_switch(
&mut lwe_ct_key_switched,
&lwe_ct,
&lwe_ksk,
&lweq_modop,
&decomposer,
);
lwe_ct_key_switched
};
// Measure noise
let mut ideal_to_lwe_sk = vec![0i32; to_lwe_n];
parties_to_lwe_sk.iter().for_each(|k| {
izip!(ideal_to_lwe_sk.iter_mut(), k.values()).for_each(|(ideal_i, s_i)| {
*ideal_i = *ideal_i + s_i;
});
});
let noise = measure_noise_lwe(&lwe_ct_key_switched, &ideal_to_lwe_sk, &lweq_modop, &m);
println!("Noise: {noise}");
let noise = measure_noise_lwe(&lwe_ct_key_switched, &ideal_to_lwe_sk, &lweq_modop, &m);
println!("Noise after key switch: {noise}");
}
} }
fn _collecitve_public_key_gen(rlwe_q: u64, parties_rlwe_sk: &[RlweSecret]) -> Vec<Vec<u64>> { fn _collecitve_public_key_gen(rlwe_q: u64, parties_rlwe_sk: &[RlweSecret]) -> Vec<Vec<u64>> {
@ -1966,7 +2069,7 @@ mod tests {
vec![pk_part_a, pk_part_b] vec![pk_part_a, pk_part_b]
} }
fn _multi_party_keygen(
fn _multi_party_all_keygen(
bool_evaluator: &BoolEvaluator<Vec<Vec<u64>>, u64, NttBackendU64, ModularOpsU64>, bool_evaluator: &BoolEvaluator<Vec<Vec<u64>>, u64, NttBackendU64, ModularOpsU64>,
no_of_parties: usize, no_of_parties: usize,
) -> ( ) -> (
@ -2053,7 +2156,7 @@ mod tests {
BoolEvaluator::<Vec<Vec<u64>>, u64, NttBackendU64, ModularOpsU64>::new(MP_BOOL_PARAMS); BoolEvaluator::<Vec<Vec<u64>>, u64, NttBackendU64, ModularOpsU64>::new(MP_BOOL_PARAMS);
let (_, collective_pk, _, _, server_key_eval, ideal_client_key) = let (_, collective_pk, _, _, server_key_eval, ideal_client_key) =
_multi_party_keygen(&bool_evaluator, 2);
_multi_party_all_keygen(&bool_evaluator, 20);
let lwe_q = bool_evaluator.parameters.lwe_q; let lwe_q = bool_evaluator.parameters.lwe_q;
let rlwe_q = bool_evaluator.parameters.rlwe_q; let rlwe_q = bool_evaluator.parameters.rlwe_q;
@ -2067,7 +2170,7 @@ mod tests {
let rlwe_decomposer = &bool_evaluator.decomposer_rlwe; let rlwe_decomposer = &bool_evaluator.decomposer_rlwe;
// test LWE ksk from RLWE -> LWE // test LWE ksk from RLWE -> LWE
if true {
if false {
let logp = 2; let logp = 2;
let mut rng = DefaultSecureRng::new(); let mut rng = DefaultSecureRng::new();
@ -2111,7 +2214,7 @@ mod tests {
} }
// Measure noise in RGSW ciphertexts of ideal LWE secrets // Measure noise in RGSW ciphertexts of ideal LWE secrets
if false {
if true {
let gadget_vec = gadget_vector( let gadget_vec = gadget_vector(
bool_evaluator.parameters.rlwe_logq, bool_evaluator.parameters.rlwe_logq,
bool_evaluator.parameters.logb_rgsw, bool_evaluator.parameters.logb_rgsw,
@ -2147,7 +2250,7 @@ mod tests {
} }
// measure noise grwoth in RLWExRGSW // measure noise grwoth in RLWExRGSW
if false {
if true {
let mut rng = DefaultSecureRng::new(); let mut rng = DefaultSecureRng::new();
let mut carry_m = vec![0u64; rlwe_n]; let mut carry_m = vec![0u64; rlwe_n];
RandomUniformDist::random_fill(&mut rng, &rlwe_q, carry_m.as_mut_slice()); RandomUniformDist::random_fill(&mut rng, &rlwe_q, carry_m.as_mut_slice());
@ -2160,7 +2263,7 @@ mod tests {
let mul_mod = let mul_mod =
|v0: &u64, v1: &u64| (((*v0 as u128 * *v1 as u128) % (rlwe_q as u128)) as u64); |v0: &u64, v1: &u64| (((*v0 as u128 * *v1 as u128) % (rlwe_q as u128)) as u64);
for i in 0..450 {
for i in 0..bool_evaluator.parameters.lwe_n {
rlwe_by_rgsw( rlwe_by_rgsw(
&mut rlwe_ct, &mut rlwe_ct,
server_key_eval.rgsw_ct_lwe_si(i), server_key_eval.rgsw_ct_lwe_si(i),
@ -2257,60 +2360,8 @@ mod tests {
let bool_evaluator = let bool_evaluator =
BoolEvaluator::<Vec<Vec<u64>>, u64, NttBackendU64, ModularOpsU64>::new(MP_BOOL_PARAMS); BoolEvaluator::<Vec<Vec<u64>>, u64, NttBackendU64, ModularOpsU64>::new(MP_BOOL_PARAMS);
let no_of_parties = 20;
let parties = (0..no_of_parties)
.map(|_| bool_evaluator.client_key())
.collect_vec();
// Collective public key
let pk_cr_seed = [0u8; 32];
let public_key_share = parties
.iter()
.map(|k| bool_evaluator.multi_party_public_key_share(pk_cr_seed, k))
.collect_vec();
let collective_pk = PublicKey::<Vec<Vec<u64>>, DefaultSecureRng, ModularOpsU64>::from(
public_key_share.as_slice(),
);
// Server key
let pbs_cr_seed = [1u8; 32];
let server_key_shares = parties
.iter()
.map(|k| bool_evaluator.multi_party_sever_key_share(pbs_cr_seed, &collective_pk.key, k))
.collect_vec();
let seeded_server_key =
aggregate_multi_party_server_key_shares::<_, _, _, ModularOpsU64, NttBackendU64>(
&server_key_shares,
&bool_evaluator.decomposer_rlwe,
);
let server_key_eval = ServerKeyEvaluationDomain::<_, DefaultSecureRng, NttBackendU64>::from(
&seeded_server_key,
);
// construct ideal rlwe sk for meauring noise
let ideal_client_key = {
let mut ideal_rlwe_sk = vec![0i32; bool_evaluator.rlwe_n()];
parties.iter().for_each(|k| {
izip!(ideal_rlwe_sk.iter_mut(), k.sk_rlwe.values()).for_each(|(ideal_i, s_i)| {
*ideal_i = *ideal_i + s_i;
});
});
let mut ideal_lwe_sk = vec![0i32; bool_evaluator.lwe_n()];
parties.iter().for_each(|k| {
izip!(ideal_lwe_sk.iter_mut(), k.sk_lwe.values()).for_each(|(ideal_i, s_i)| {
*ideal_i = *ideal_i + s_i;
});
});
ClientKey {
sk_lwe: LweSecret {
values: ideal_lwe_sk,
},
sk_rlwe: RlweSecret {
values: ideal_rlwe_sk,
},
}
};
let (parties, collective_pk, _, _, server_key_eval, ideal_client_key) =
_multi_party_all_keygen(&bool_evaluator, 50);
// PBS // PBS
let mut scratch_lwen_plus1 = vec![0u64; bool_evaluator.parameters.lwe_n + 1]; let mut scratch_lwen_plus1 = vec![0u64; bool_evaluator.parameters.lwe_n + 1];
@ -2322,10 +2373,10 @@ mod tests {
let mut m0 = true; let mut m0 = true;
let mut m1 = false; let mut m1 = false;
for _ in 0..500 {
let lwe0 = bool_evaluator.pk_encrypt(&collective_pk.key, m0);
let lwe1 = bool_evaluator.pk_encrypt(&collective_pk.key, m1);
let mut lwe0 = bool_evaluator.pk_encrypt(&collective_pk.key, m0);
let mut lwe1 = bool_evaluator.pk_encrypt(&collective_pk.key, m1);
for _ in 0..2000 {
let lwe_out = bool_evaluator.nand( let lwe_out = bool_evaluator.nand(
&lwe0, &lwe0,
&lwe1, &lwe1,
@ -2338,6 +2389,45 @@ mod tests {
// measure noise // measure noise
{ {
let noise0 = {
let ideal = if m0 {
bool_evaluator.rlweq_by8
} else {
bool_evaluator.rlwe_q() - bool_evaluator.rlweq_by8
};
let n = measure_noise_lwe(
&lwe0,
ideal_client_key.sk_rlwe.values(),
&bool_evaluator.rlwe_modop,
&ideal,
);
let v = decrypt_lwe(
&lwe0,
ideal_client_key.sk_rlwe.values(),
&bool_evaluator.rlwe_modop,
);
(n, v)
};
let noise1 = {
let ideal = if m1 {
bool_evaluator.rlweq_by8
} else {
bool_evaluator.rlwe_q() - bool_evaluator.rlweq_by8
};
let n = measure_noise_lwe(
&lwe1,
ideal_client_key.sk_rlwe.values(),
&bool_evaluator.rlwe_modop,
&ideal,
);
let v = decrypt_lwe(
&lwe1,
ideal_client_key.sk_rlwe.values(),
&bool_evaluator.rlwe_modop,
);
(n, v)
};
// Trace PBS // Trace PBS
PBSTracer::with_local(|t| { PBSTracer::with_local(|t| {
t.trace( t.trace(
@ -2347,18 +2437,29 @@ mod tests {
) )
}); });
let ideal_m = if m_expected {
bool_evaluator.rlweq_by8
} else {
bool_evaluator.parameters.rlwe_q - bool_evaluator.rlweq_by8
let noise_out = {
let ideal_m = if m_expected {
bool_evaluator.rlweq_by8
} else {
bool_evaluator.parameters.rlwe_q - bool_evaluator.rlweq_by8
};
let n = measure_noise_lwe(
&lwe_out,
ideal_client_key.sk_rlwe.values(),
&bool_evaluator.rlwe_modop,
&ideal_m,
);
let v = decrypt_lwe(
&lwe_out,
ideal_client_key.sk_rlwe.values(),
&bool_evaluator.rlwe_modop,
);
(n, v)
}; };
let noise = measure_noise_lwe(
&lwe_out,
ideal_client_key.sk_rlwe.values(),
&bool_evaluator.rlwe_modop,
&ideal_m,
dbg!(m0, m1, m_expected);
println!(
"ct0 (noise, message): {:?} \n ct1 (noise, message): {:?} \n PBS (noise, message): {:?}", noise0, noise1, noise_out
); );
println!("Noise: {noise}");
} }
// multi-party decrypt // multi-party decrypt
@ -2373,6 +2474,9 @@ mod tests {
assert!(m_expected == m_back, "Expected {m_expected}, got {m_back}"); assert!(m_expected == m_back, "Expected {m_expected}, got {m_back}");
m1 = m0; m1 = m0;
m0 = m_expected; m0 = m_expected;
lwe1 = lwe0;
lwe0 = lwe_out;
} }
} }
} }

+ 9
- 10
src/bool/parameters.rs

@ -38,17 +38,16 @@ pub(super) const SP_BOOL_PARAMS: BoolParameters = BoolParameters:: {
}; };
pub(super) const MP_BOOL_PARAMS: BoolParameters<u64> = BoolParameters::<u64> { pub(super) const MP_BOOL_PARAMS: BoolParameters<u64> = BoolParameters::<u64> {
rlwe_q: 18014398509404161,
rlwe_logq: 54,
lwe_q: 1 << 18,
lwe_logq: 18,
// TODO(Jay:) why does this fail when q=1<<11?
rlwe_q: 1152921504606830593,
rlwe_logq: 60,
lwe_q: 1 << 20,
lwe_logq: 20,
br_q: 1 << 11, br_q: 1 << 11,
rlwe_n: 1 << 11, rlwe_n: 1 << 11,
lwe_n: 200,
d_rgsw: 5,
logb_rgsw: 10,
d_lwe: 4,
lwe_n: 500,
d_rgsw: 4,
logb_rgsw: 12,
d_lwe: 5,
logb_lwe: 4, logb_lwe: 4,
g: 5, g: 5,
w: 1, w: 1,
@ -60,7 +59,7 @@ mod tests {
#[test] #[test]
fn find_prime() { fn find_prime() {
let bits = 54;
let bits = 61;
let ring_size = 1 << 11; let ring_size = 1 << 11;
let prime = generate_prime(bits, ring_size * 2, 1 << bits).unwrap(); let prime = generate_prime(bits, ring_size * 2, 1 << bits).unwrap();
dbg!(prime); dbg!(prime);

+ 4
- 4
src/decomposer.rs

@ -158,9 +158,9 @@ mod tests {
#[test] #[test]
fn decomposition_works() { fn decomposition_works() {
let logq = 60;
let logb = 5;
let d = 12;
let logq = 55;
let logb = 9;
let d = 6;
let mut rng = thread_rng(); let mut rng = thread_rng();
@ -174,7 +174,7 @@ mod tests {
}; };
let decomposer = DefaultDecomposer::new(q, logb, d); let decomposer = DefaultDecomposer::new(q, logb, d);
let modq_op = ModularOpsU64::new(q); let modq_op = ModularOpsU64::new(q);
for _ in 0..100 {
for _ in 0..1000 {
let value = rng.gen_range(0..q); let value = rng.gen_range(0..q);
let limbs = decomposer.decompose(&value); let limbs = decomposer.decompose(&value);
let value_back = decomposer.recompose(&limbs, &modq_op); let value_back = decomposer.recompose(&limbs, &modq_op);

+ 2
- 4
src/lwe.rs

@ -176,8 +176,6 @@ pub fn lwe_ksk_keygen<
b = operator.add(&b, &e); b = operator.add(&b, &e);
*lwe_b = b; *lwe_b = b;
// dbg!(&lwe.as_mut(), &f);
}) })
}, },
); );
@ -323,13 +321,13 @@ mod tests {
#[test] #[test]
fn key_switch_works() { fn key_switch_works() {
let logq = 16;
let logq = 18;
let logp = 2; let logp = 2;
let q = 1u64 << logq; let q = 1u64 << logq;
let lwe_in_n = 2048; let lwe_in_n = 2048;
let lwe_out_n = 493; let lwe_out_n = 493;
let d_ks = 3; let d_ks = 3;
let logb = 4;
let logb = 6;
let lwe_sk_in = LweSecret::random(lwe_in_n >> 1, lwe_in_n); let lwe_sk_in = LweSecret::random(lwe_in_n >> 1, lwe_in_n);
let lwe_sk_out = LweSecret::random(lwe_out_n >> 1, lwe_out_n); let lwe_sk_out = LweSecret::random(lwe_out_n >> 1, lwe_out_n);

+ 2
- 2
src/random.rs

@ -141,7 +141,7 @@ impl RandomGaussianDist<[u64]> for DefaultSecureRng {
type Parameters = u64; type Parameters = u64;
fn random_fill(&mut self, parameters: &Self::Parameters, container: &mut [u64]) { fn random_fill(&mut self, parameters: &Self::Parameters, container: &mut [u64]) {
izip!( izip!(
rand_distr::Normal::new(0.0, 3.2f64)
rand_distr::Normal::new(0.0, 3.19f64)
.unwrap() .unwrap()
.sample_iter(&mut self.rng), .sample_iter(&mut self.rng),
container.iter_mut() container.iter_mut()
@ -162,7 +162,7 @@ impl RandomGaussianDist<[u32]> for DefaultSecureRng {
type Parameters = u32; type Parameters = u32;
fn random_fill(&mut self, parameters: &Self::Parameters, container: &mut [u32]) { fn random_fill(&mut self, parameters: &Self::Parameters, container: &mut [u32]) {
izip!( izip!(
rand_distr::Normal::new(0.0, 3.2f32)
rand_distr::Normal::new(0.0, 3.19f32)
.unwrap() .unwrap()
.sample_iter(&mut self.rng), .sample_iter(&mut self.rng),
container.iter_mut() container.iter_mut()

+ 202
- 150
src/rgsw.rs

@ -463,130 +463,6 @@ pub(crate) fn generate_auto_map(ring_size: usize, k: isize) -> (Vec, Vec<
(auto_map_index, auto_sign_index) (auto_map_index, auto_sign_index)
} }
/// Generates RLWE Key switching key to key switch ciphertext RLWE_{from_s}(m)
/// to RLWE_{to_s}(m).
///
/// Key switching equals
/// \sum decompose(c_1)_i * RLWE_{to_s}(\beta^i -from_s)
/// Hence, key switchin key equals RLWE'(-from_s) = RLWE(-from_s), RLWE(beta^1
/// -from_s), ..., RLWE(beta^{d-1} -from_s).
///
/// - ksk_out: Output Key switching key. Key switching key stores only part B
/// polynomials of ksk RLWE ciphertexts (i.e. RLWE'_B(-from_s)) in coefficient
/// domain
/// - neg_from_s: Negative of secret polynomial to key switch from
/// - to_s: secret polynomial to key switch to.
pub(crate) fn rlwe_ksk_gen<
Mmut: MatrixMut + MatrixEntity,
ModOp: ArithmeticOps<Element = Mmut::MatElement> + VectorOps<Element = Mmut::MatElement>,
NttOp: Ntt<Element = Mmut::MatElement>,
R: RandomGaussianDist<[Mmut::MatElement], Parameters = Mmut::MatElement>,
PR: RandomUniformDist<[Mmut::MatElement], Parameters = Mmut::MatElement>,
>(
ksk_out: &mut Mmut,
neg_from_s: Mmut::R,
mut to_s: Mmut::R,
gadget_vector: &[Mmut::MatElement],
mod_op: &ModOp,
ntt_op: &NttOp,
p_rng: &mut PR,
rng: &mut R,
) where
<Mmut as Matrix>::R: RowMut,
{
let ring_size = neg_from_s.as_ref().len();
let d = gadget_vector.len();
assert!(ksk_out.dimension() == (d, ring_size));
let q = ArithmeticOps::modulus(mod_op);
ntt_op.forward(to_s.as_mut());
// RLWE'_{to_s}(-from_s)
let mut part_a = {
let mut a = Mmut::zeros(d, ring_size);
a.iter_rows_mut()
.for_each(|ai| RandomUniformDist::random_fill(p_rng, &q, ai.as_mut()));
a
};
izip!(
part_a.iter_rows_mut(),
ksk_out.iter_rows_mut(),
gadget_vector.iter(),
)
.for_each(|(ai, bi, beta_i)| {
// si * ai
ntt_op.forward(ai.as_mut());
mod_op.elwise_mul_mut(ai.as_mut(), to_s.as_ref());
ntt_op.backward(ai.as_mut());
// ei + to_s*ai
RandomGaussianDist::random_fill(rng, &q, bi.as_mut());
mod_op.elwise_add_mut(bi.as_mut(), ai.as_ref());
// beta_i * -from_s
// use ai as scratch space
mod_op.elwise_scalar_mul(ai.as_mut(), neg_from_s.as_ref(), beta_i);
// bi = ei + to_s*ai + beta_i*-from_s
mod_op.elwise_add_mut(bi.as_mut(), ai.as_ref());
});
}
pub(crate) fn galois_key_gen<
Mmut: MatrixMut + MatrixEntity,
ModOp: ArithmeticOps<Element = Mmut::MatElement> + VectorOps<Element = Mmut::MatElement>,
NttOp: Ntt<Element = Mmut::MatElement>,
S,
R: RandomGaussianDist<[Mmut::MatElement], Parameters = Mmut::MatElement>,
PR: RandomUniformDist<[Mmut::MatElement], Parameters = Mmut::MatElement>,
>(
ksk_out: &mut Mmut,
s: &[S],
auto_k: isize,
gadget_vector: &[Mmut::MatElement],
mod_op: &ModOp,
ntt_op: &NttOp,
p_rng: &mut PR,
rng: &mut R,
) where
<Mmut as Matrix>::R: RowMut,
Mmut::R: TryConvertFrom<[S], Parameters = Mmut::MatElement> + RowEntity,
Mmut::MatElement: Copy + Sub<Output = Mmut::MatElement>,
{
let ring_size = s.len();
let (auto_map_index, auto_map_sign) = generate_auto_map(ring_size, auto_k);
let q = ArithmeticOps::modulus(mod_op);
// s(X) -> -s(X^k)
let s = Mmut::R::try_convert_from(s, &q);
let mut neg_s_auto = Mmut::R::zeros(s.as_ref().len());
izip!(s.as_ref(), auto_map_index.iter(), auto_map_sign.iter()).for_each(
|(el, to_index, sign)| {
// if sign is +ve (true), then negate because we need -s(X) (i.e. do the
// opposite than the usual case)
if *sign {
neg_s_auto.as_mut()[*to_index] = q - *el;
} else {
neg_s_auto.as_mut()[*to_index] = *el;
}
},
);
// Ksk from -s(X^k) to s(X)
rlwe_ksk_gen(
ksk_out,
neg_s_auto,
s,
gadget_vector,
mod_op,
ntt_op,
p_rng,
rng,
);
}
pub(crate) fn routine<R: RowMut, ModOp: VectorOps<Element = R::Element>>( pub(crate) fn routine<R: RowMut, ModOp: VectorOps<Element = R::Element>>(
write_to_row: &mut [R::Element], write_to_row: &mut [R::Element],
matrix_a: &[R], matrix_a: &[R],
@ -958,8 +834,9 @@ pub(crate) fn secret_key_encrypt_rgsw<
p_rng: &mut PR, p_rng: &mut PR,
rng: &mut R, rng: &mut R,
) where ) where
<Mmut as Matrix>::R: RowMut + RowEntity + TryConvertFrom<[S], Parameters = Mmut::MatElement>,
Mmut::MatElement: Copy,
<Mmut as Matrix>::R:
RowMut + RowEntity + TryConvertFrom<[S], Parameters = Mmut::MatElement> + Debug,
Mmut::MatElement: Copy + Debug,
{ {
let d = gadget_vector.len(); let d = gadget_vector.len();
let q = mod_op.modulus(); let q = mod_op.modulus();
@ -1149,6 +1026,130 @@ pub(crate) fn public_key_encrypt_rgsw<
}); });
} }
/// Generates RLWE Key switching key to key switch ciphertext RLWE_{from_s}(m)
/// to RLWE_{to_s}(m).
///
/// Key switching equals
/// \sum decompose(c_1)_i * RLWE_{to_s}(\beta^i -from_s)
/// Hence, key switchin key equals RLWE'(-from_s) = RLWE(-from_s), RLWE(beta^1
/// -from_s), ..., RLWE(beta^{d-1} -from_s).
///
/// - ksk_out: Output Key switching key. Key switching key stores only part B
/// polynomials of ksk RLWE ciphertexts (i.e. RLWE'_B(-from_s)) in coefficient
/// domain
/// - neg_from_s: Negative of secret polynomial to key switch from
/// - to_s: secret polynomial to key switch to.
pub(crate) fn rlwe_ksk_gen<
Mmut: MatrixMut + MatrixEntity,
ModOp: ArithmeticOps<Element = Mmut::MatElement> + VectorOps<Element = Mmut::MatElement>,
NttOp: Ntt<Element = Mmut::MatElement>,
R: RandomGaussianDist<[Mmut::MatElement], Parameters = Mmut::MatElement>,
PR: RandomUniformDist<[Mmut::MatElement], Parameters = Mmut::MatElement>,
>(
ksk_out: &mut Mmut,
neg_from_s: Mmut::R,
mut to_s: Mmut::R,
gadget_vector: &[Mmut::MatElement],
mod_op: &ModOp,
ntt_op: &NttOp,
p_rng: &mut PR,
rng: &mut R,
) where
<Mmut as Matrix>::R: RowMut,
{
let ring_size = neg_from_s.as_ref().len();
let d = gadget_vector.len();
assert!(ksk_out.dimension() == (d, ring_size));
let q = ArithmeticOps::modulus(mod_op);
ntt_op.forward(to_s.as_mut());
// RLWE'_{to_s}(-from_s)
let mut part_a = {
let mut a = Mmut::zeros(d, ring_size);
a.iter_rows_mut()
.for_each(|ai| RandomUniformDist::random_fill(p_rng, &q, ai.as_mut()));
a
};
izip!(
part_a.iter_rows_mut(),
ksk_out.iter_rows_mut(),
gadget_vector.iter(),
)
.for_each(|(ai, bi, beta_i)| {
// si * ai
ntt_op.forward(ai.as_mut());
mod_op.elwise_mul_mut(ai.as_mut(), to_s.as_ref());
ntt_op.backward(ai.as_mut());
// ei + to_s*ai
RandomGaussianDist::random_fill(rng, &q, bi.as_mut());
mod_op.elwise_add_mut(bi.as_mut(), ai.as_ref());
// beta_i * -from_s
// use ai as scratch space
mod_op.elwise_scalar_mul(ai.as_mut(), neg_from_s.as_ref(), beta_i);
// bi = ei + to_s*ai + beta_i*-from_s
mod_op.elwise_add_mut(bi.as_mut(), ai.as_ref());
});
}
pub(crate) fn galois_key_gen<
Mmut: MatrixMut + MatrixEntity,
ModOp: ArithmeticOps<Element = Mmut::MatElement> + VectorOps<Element = Mmut::MatElement>,
NttOp: Ntt<Element = Mmut::MatElement>,
S,
R: RandomGaussianDist<[Mmut::MatElement], Parameters = Mmut::MatElement>,
PR: RandomUniformDist<[Mmut::MatElement], Parameters = Mmut::MatElement>,
>(
ksk_out: &mut Mmut,
s: &[S],
auto_k: isize,
gadget_vector: &[Mmut::MatElement],
mod_op: &ModOp,
ntt_op: &NttOp,
p_rng: &mut PR,
rng: &mut R,
) where
<Mmut as Matrix>::R: RowMut,
Mmut::R: TryConvertFrom<[S], Parameters = Mmut::MatElement> + RowEntity,
Mmut::MatElement: Copy + Sub<Output = Mmut::MatElement>,
{
let ring_size = s.len();
let (auto_map_index, auto_map_sign) = generate_auto_map(ring_size, auto_k);
let q = ArithmeticOps::modulus(mod_op);
// s(X) -> -s(X^k)
let s = Mmut::R::try_convert_from(s, &q);
let mut neg_s_auto = Mmut::R::zeros(s.as_ref().len());
izip!(s.as_ref(), auto_map_index.iter(), auto_map_sign.iter()).for_each(
|(el, to_index, sign)| {
// if sign is +ve (true), then negate because we need -s(X) (i.e. do the
// opposite than the usual case)
if *sign {
neg_s_auto.as_mut()[*to_index] = q - *el;
} else {
neg_s_auto.as_mut()[*to_index] = *el;
}
},
);
// Ksk from -s(X^k) to s(X)
rlwe_ksk_gen(
ksk_out,
neg_s_auto,
s,
gadget_vector,
mod_op,
ntt_op,
p_rng,
rng,
);
}
/// Encrypt polynomial m(X) as RLWE ciphertext. /// Encrypt polynomial m(X) as RLWE ciphertext.
/// ///
/// - rlwe_out: returned RLWE ciphertext RLWE(m) in coefficient domain. RLWE /// - rlwe_out: returned RLWE ciphertext RLWE(m) in coefficient domain. RLWE
@ -1199,7 +1200,8 @@ pub(crate) fn secret_key_encrypt_rlwe<
} }
pub(crate) fn public_key_encrypt_rlwe< pub(crate) fn public_key_encrypt_rlwe<
M: MatrixMut,
M: Matrix,
Mmut: MatrixMut<MatElement = M::MatElement>,
ModOp: VectorOps<Element = M::MatElement>, ModOp: VectorOps<Element = M::MatElement>,
NttOp: Ntt<Element = M::MatElement>, NttOp: Ntt<Element = M::MatElement>,
S, S,
@ -1208,14 +1210,14 @@ pub(crate) fn public_key_encrypt_rlwe<
+ RandomUniformDist<[u8], Parameters = u8> + RandomUniformDist<[u8], Parameters = u8>
+ RandomUniformDist<usize, Parameters = usize>, + RandomUniformDist<usize, Parameters = usize>,
>( >(
rlwe_out: &mut M,
rlwe_out: &mut Mmut,
pk: &M, pk: &M,
m: &[M::MatElement], m: &[M::MatElement],
mod_op: &ModOp, mod_op: &ModOp,
ntt_op: &NttOp, ntt_op: &NttOp,
rng: &mut R, rng: &mut R,
) where ) where
<M as Matrix>::R: RowMut + TryConvertFrom<[S], Parameters = M::MatElement> + RowEntity,
<Mmut as Matrix>::R: RowMut + TryConvertFrom<[S], Parameters = M::MatElement> + RowEntity,
M::MatElement: Copy, M::MatElement: Copy,
S: Zero + Signed + Copy, S: Zero + Signed + Copy,
{ {
@ -1226,12 +1228,12 @@ pub(crate) fn public_key_encrypt_rlwe<
let mut u = vec![S::zero(); ring_size]; let mut u = vec![S::zero(); ring_size];
fill_random_ternary_secret_with_hamming_weight(u.as_mut(), ring_size >> 1, rng); fill_random_ternary_secret_with_hamming_weight(u.as_mut(), ring_size >> 1, rng);
let mut u = M::R::try_convert_from(&u, &q);
let mut u = Mmut::R::try_convert_from(&u, &q);
ntt_op.forward(u.as_mut()); ntt_op.forward(u.as_mut());
let mut ua = M::R::zeros(ring_size);
let mut ua = Mmut::R::zeros(ring_size);
ua.as_mut().copy_from_slice(pk.get_row_slice(0)); ua.as_mut().copy_from_slice(pk.get_row_slice(0));
let mut ub = M::R::zeros(ring_size);
let mut ub = Mmut::R::zeros(ring_size);
ub.as_mut().copy_from_slice(pk.get_row_slice(1)); ub.as_mut().copy_from_slice(pk.get_row_slice(1));
// a*u // a*u
@ -1419,8 +1421,9 @@ pub(crate) mod tests {
}; };
use super::{ use super::{
decrypt_rlwe, galois_auto, galois_key_gen, generate_auto_map, rgsw_by_rgsw_inplace,
rlwe_by_rgsw, secret_key_encrypt_rgsw, secret_key_encrypt_rlwe, RlweSecret,
decrypt_rlwe, galois_auto, galois_key_gen, generate_auto_map, public_key_encrypt_rlwe,
rgsw_by_rgsw_inplace, rlwe_by_rgsw, secret_key_encrypt_rgsw, secret_key_encrypt_rlwe,
RlweSecret,
}; };
#[test] #[test]
@ -1477,6 +1480,9 @@ pub(crate) mod tests {
.map(|v| (((*v as f64 * p as f64) / q as f64).round() as u64) % p) .map(|v| (((*v as f64 * p as f64) / q as f64).round() as u64) % p)
.collect_vec(); .collect_vec();
assert_eq!(m0, m_back); assert_eq!(m0, m_back);
let noise = measure_noise(&rlwe_in_ct, &encoded_m, &ntt_op, &mod_op, s.values());
println!("Noise: {noise}");
} }
#[test] #[test]
@ -1642,11 +1648,11 @@ pub(crate) mod tests {
#[test] #[test]
fn rlwe_by_rgsw_noise_growth() { fn rlwe_by_rgsw_noise_growth() {
let logq = 60;
let ring_size = 1 << 11;
let logq = 28;
let ring_size = 1 << 10;
let q = generate_prime(logq, ring_size * 2, 1u64 << logq).unwrap(); let q = generate_prime(logq, ring_size * 2, 1u64 << logq).unwrap();
let d_rgsw = 7;
let logb = 8;
let d_rgsw = 2;
let logb = 7;
let s = RlweSecret::random((ring_size >> 1) as usize, ring_size as usize); let s = RlweSecret::random((ring_size >> 1) as usize, ring_size as usize);
@ -1685,6 +1691,7 @@ pub(crate) mod tests {
} }
} }
// Encrypt m as RGSW ciphertext RGSW(m) using supplied public key
fn _pk_encrypt_rgsw( fn _pk_encrypt_rgsw(
m: &[u64], m: &[u64],
public_key: &RlwePublicKey<Vec<Vec<u64>>, DefaultSecureRng>, public_key: &RlwePublicKey<Vec<Vec<u64>>, DefaultSecureRng>,
@ -1821,8 +1828,8 @@ pub(crate) mod tests {
let ring_size = 1 << 11; let ring_size = 1 << 11;
let q = generate_prime(logq, ring_size, 1u64 << logq).unwrap(); let q = generate_prime(logq, ring_size, 1u64 << logq).unwrap();
let p = 1u64 << logp; let p = 1u64 << logp;
let d_rgsw = 15;
let logb = 4;
let d_rgsw = 3;
let logb = 15;
let s = RlweSecret::random((ring_size >> 1) as usize, ring_size as usize); let s = RlweSecret::random((ring_size >> 1) as usize, ring_size as usize);
@ -1832,6 +1839,8 @@ pub(crate) mod tests {
let gadget_vector = gadget_vector(logq, logb, d_rgsw); let gadget_vector = gadget_vector(logq, logb, d_rgsw);
let decomposer = DefaultDecomposer::new(q, logb, d_rgsw); let decomposer = DefaultDecomposer::new(q, logb, d_rgsw);
let mul_mod = |a: &u64, b: &u64| ((*a as u128 * *b as u128) % q as u128) as u64;
// Public Key // Public Key
let public_key = { let public_key = {
let mut pk_seed = [0u8; 32]; let mut pk_seed = [0u8; 32];
@ -1856,11 +1865,23 @@ pub(crate) mod tests {
// RGSW(carry_m) // RGSW(carry_m)
let mut rgsw_carrym = let mut rgsw_carrym =
_pk_encrypt_rgsw(&carry_m, &public_key, &gadget_vector, &mod_op, &ntt_op); _pk_encrypt_rgsw(&carry_m, &public_key, &gadget_vector, &mod_op, &ntt_op);
// let mut rgsw_carrym = {
// let mut rgsw_eval =
// _sk_encrypt_rgsw(&carry_m, s.values(), &gadget_vector, &mod_op,
// &ntt_op); rgsw_eval
// .data
// .iter_mut()
// .for_each(|ri| ntt_op.backward(ri.as_mut()));
// rgsw_eval.data
// };
println!("########### Noise RGSW(carrym) at start ###########");
_measure_noise_rgsw(&rgsw_carrym.data, &carry_m, s.values(), &gadget_vector, q);
let mut scratch_matrix_d_plus_rgsw_by_ring = let mut scratch_matrix_d_plus_rgsw_by_ring =
vec![vec![0u64; ring_size as usize]; d_rgsw + (d_rgsw * 4)]; vec![vec![0u64; ring_size as usize]; d_rgsw + (d_rgsw * 4)];
for i in 0..100 {
for i in 0..10 {
let mut m = vec![0u64; ring_size as usize]; let mut m = vec![0u64; ring_size as usize];
m[thread_rng().gen_range(0..ring_size) as usize] = q - 1; m[thread_rng().gen_range(0..ring_size) as usize] = q - 1;
let rgsw_m = { let rgsw_m = {
@ -1879,22 +1900,54 @@ pub(crate) mod tests {
); );
// measure noise // measure noise
let mul_mod = |a: &u64, b: &u64| ((*a as u128 * *b as u128) % q as u128) as u64;
carry_m = negacyclic_mul(&carry_m, &m, mul_mod, q); carry_m = negacyclic_mul(&carry_m, &m, mul_mod, q);
println!("########### Noise RGSW(carrym) in {i}^th loop ###########"); println!("########### Noise RGSW(carrym) in {i}^th loop ###########");
_measure_noise_rgsw(&rgsw_carrym.data, &carry_m, s.values(), &gadget_vector, q); _measure_noise_rgsw(&rgsw_carrym.data, &carry_m, s.values(), &gadget_vector, q);
} }
// {
// // RLWE(m) x RGSW(carry_m)
// let mut m = vec![0u64; ring_size as usize];
// RandomUniformDist::random_fill(&mut rng, &q, m.as_mut_slice());
// let mut rlwe_ct = RlweCiphertext::<_,
// DefaultSecureRng>::from_raw( vec![vec![0u64;
// ring_size as usize]; 2], false,
// );
// let mut scratch_matrix_dplus2_ring = vec![vec![0u64; ring_size as
// usize]; d_rgsw + 2]; public_key_encrypt_rlwe(
// &mut rlwe_ct,
// &public_key.data,
// &m,
// &mod_op,
// &ntt_op,
// &mut rng,
// );
// rlwe_by_rgsw(
// &mut rlwe_ct,
// &RgswCiphertextEvaluationDomain::<_, DefaultSecureRng,
// NttBackendU64>::from( &rgsw_carrym,
// )
// .data,
// &mut scratch_matrix_dplus2_ring,
// &decomposer,
// &ntt_op,
// &mod_op,
// );
// let m_expected = negacyclic_mul(&carry_m, &m, mul_mod, q);
// let noise = measure_noise(&rlwe_ct, &m_expected, &ntt_op,
// &mod_op, s.values()); println!("RLWE(m) x RGSW(carry_m):
// {noise}"); }
} }
#[test] #[test]
fn sk_rgsw_by_rgsw() { fn sk_rgsw_by_rgsw() {
let logq = 31;
let logq = 60;
let logp = 2; let logp = 2;
let ring_size = 1 << 10;
let ring_size = 1 << 11;
let q = generate_prime(logq, ring_size, 1u64 << logq).unwrap(); let q = generate_prime(logq, ring_size, 1u64 << logq).unwrap();
let p = 1u64 << logp; let p = 1u64 << logp;
let d_rgsw = 4;
let logb = 7;
let d_rgsw = 3;
let logb = 15;
let s = RlweSecret::random((ring_size >> 1) as usize, ring_size as usize); let s = RlweSecret::random((ring_size >> 1) as usize, ring_size as usize);
@ -1918,11 +1971,13 @@ pub(crate) mod tests {
.for_each(|ri| ntt_op.backward(ri.as_mut())); .for_each(|ri| ntt_op.backward(ri.as_mut()));
rgsw_eval.data rgsw_eval.data
}; };
println!("########### Noise RGSW(carrym) at start ###########");
_measure_noise_rgsw(&rgsw_carrym, &carry_m, s.values(), &gadget_vector, q);
let mut scratch_matrix_d_plus_rgsw_by_ring = let mut scratch_matrix_d_plus_rgsw_by_ring =
vec![vec![0u64; ring_size as usize]; d_rgsw + (d_rgsw * 4)]; vec![vec![0u64; ring_size as usize]; d_rgsw + (d_rgsw * 4)];
for i in 0..1000 {
for i in 0..10 {
let mut m = vec![0u64; ring_size as usize]; let mut m = vec![0u64; ring_size as usize];
m[thread_rng().gen_range(0..ring_size) as usize] = if (i & 1) == 1 { q - 1 } else { 1 }; m[thread_rng().gen_range(0..ring_size) as usize] = if (i & 1) == 1 { q - 1 } else { 1 };
let rgsw_m = _sk_encrypt_rgsw(&m, s.values(), &gadget_vector, &mod_op, &ntt_op); let rgsw_m = _sk_encrypt_rgsw(&m, s.values(), &gadget_vector, &mod_op, &ntt_op);
@ -2057,9 +2112,6 @@ pub(crate) mod tests {
println!("Ksk noise: {noise}"); 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); assert_eq!(m_k_back, m_k);
// dbg!(m_k_back, m_k, q);
} }
} }

Loading…
Cancel
Save