Browse Source

RLWExRGSW error grwoth behaves weird for primes > 30 bits

par-agg-key-shares
Janmajaya Mall 11 months ago
parent
commit
b4519b7496
4 changed files with 550 additions and 277 deletions
  1. +256
    -177
      src/bool/evaluator.rs
  2. +6
    -6
      src/bool/parameters.rs
  3. +14
    -9
      src/decomposer.rs
  4. +274
    -85
      src/rgsw.rs

+ 256
- 177
src/bool/evaluator.rs

@ -756,7 +756,7 @@ where
let mut rgsw_si = M::zeros(self.parameters.d_rgsw * 3, ring_size); let mut rgsw_si = M::zeros(self.parameters.d_rgsw * 3, ring_size);
secret_key_encrypt_rgsw( secret_key_encrypt_rgsw(
&mut rgsw_si, &mut rgsw_si,
&m,
m.as_ref(),
&d_rgsw_gadget_vec, &d_rgsw_gadget_vec,
sk_rlwe.values(), sk_rlwe.values(),
&self.rlwe_modop, &self.rlwe_modop,
@ -1635,8 +1635,8 @@ mod tests {
ntt::NttBackendU64, ntt::NttBackendU64,
random::DEFAULT_RNG, random::DEFAULT_RNG,
rgsw::{ rgsw::{
secret_key_encrypt_rlwe, RgswCiphertextEvaluationDomain, SeededRgswCiphertext,
SeededRlweCiphertext,
self, measure_noise, secret_key_encrypt_rlwe, tests::_measure_noise_rgsw,
RgswCiphertextEvaluationDomain, SeededRgswCiphertext, SeededRlweCiphertext,
}, },
utils::negacyclic_mul, utils::negacyclic_mul,
}; };
@ -1803,11 +1803,11 @@ mod tests {
} }
#[test] #[test]
fn trial_mp() {
fn mp_key_correcntess() {
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 = 2;
let no_of_parties = 5;
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();
@ -1863,58 +1863,95 @@ mod tests {
}; };
// test LWE ksk from RLWE -> LWE // test LWE ksk from RLWE -> LWE
// {
// let lwe_q = bool_evaluator.parameters.lwe_q;
// let lwe_logq = bool_evaluator.parameters.lwe_logq;
// let lwe_n = bool_evaluator.parameters.lwe_n;
// let rlwe_n = bool_evaluator.parameters.rlwe_n;
// let logp = 2;
// let lwe_modop = &bool_evaluator.lwe_modop;
// let mut rng = DefaultSecureRng::new();
// let m = 1;
// let encoded_m = m << (lwe_logq - logp);
// // Encrypt
// let mut lwe_ct = vec![0u64; rlwe_n + 1];
// encrypt_lwe(
// &mut lwe_ct,
// &encoded_m,
// ideal_client_key.sk_rlwe.values(),
// lwe_modop,
// &mut rng,
// );
if false {
let lwe_q = bool_evaluator.parameters.lwe_q;
let lwe_logq = bool_evaluator.parameters.lwe_logq;
let lwe_n = bool_evaluator.parameters.lwe_n;
let rlwe_n = bool_evaluator.parameters.rlwe_n;
let logp = 2;
let lwe_modop = &bool_evaluator.lwe_modop;
let mut rng = DefaultSecureRng::new();
// // key switch
// let lwe_decomposer = &bool_evaluator.decomposer_lwe;
// let mut lwe_out = vec![0u64; lwe_n + 1];
// lwe_key_switch(
// &mut lwe_out,
// &lwe_ct,
// &server_key_eval.lwe_ksk,
// lwe_modop,
// lwe_decomposer,
// );
let m = 1;
let encoded_m = m << (lwe_logq - logp);
// Encrypt
let mut lwe_ct = vec![0u64; rlwe_n + 1];
encrypt_lwe(
&mut lwe_ct,
&encoded_m,
ideal_client_key.sk_rlwe.values(),
lwe_modop,
&mut rng,
);
// key switch
let lwe_decomposer = &bool_evaluator.decomposer_lwe;
let mut lwe_out = vec![0u64; lwe_n + 1];
lwe_key_switch(
&mut lwe_out,
&lwe_ct,
&server_key_eval.lwe_ksk,
lwe_modop,
lwe_decomposer,
);
// let encoded_m_back = decrypt_lwe(&lwe_out,
// ideal_client_key.sk_lwe.values(), lwe_modop); let m_back =
// ((encoded_m_back as f64 * (1 << logp) as f64) / (lwe_q as
// f64)).round() as u64; dbg!(m_back, m);
let encoded_m_back = decrypt_lwe(&lwe_out, ideal_client_key.sk_lwe.values(), lwe_modop);
let m_back =
((encoded_m_back as f64 * (1 << logp) as f64) / (lwe_q as f64)).round() as u64;
dbg!(m_back, m);
// let noise = measure_noise_lwe(
// &lwe_out,
// ideal_client_key.sk_lwe.values(),
// lwe_modop,
// &encoded_m,
// );
let noise = measure_noise_lwe(
&lwe_out,
ideal_client_key.sk_lwe.values(),
lwe_modop,
&encoded_m,
);
// println!("Noise: {noise}");
// }
println!("Noise: {noise}");
}
{ {
let rlwe_n = bool_evaluator.parameters.rlwe_n;
let rlwe_q = bool_evaluator.parameters.rlwe_q;
let gadget_vec = gadget_vector(
bool_evaluator.parameters.rlwe_logq,
bool_evaluator.parameters.logb_rgsw,
bool_evaluator.parameters.d_rgsw,
);
let rlwe_nttop = &bool_evaluator.rlwe_nttop;
for i in 0..20 {
// measure noise in RGSW(s[i])
let si = ideal_client_key.sk_lwe.values[i];
let mut si_poly = vec![0u64; rlwe_n];
if si < 0 {
si_poly[rlwe_n - (si.abs() as usize)] = rlwe_q - 1;
} else {
si_poly[(si.abs() as usize)] = 1;
}
let mut rgsw_si = server_key_eval.rgsw_cts[i].clone();
rgsw_si
.iter_mut()
.for_each(|ri| rlwe_nttop.backward(ri.as_mut()));
println!("####### Noise in RGSW(X^s_{i}) #######");
let noise = _measure_noise_rgsw(
&rgsw_si,
&si_poly,
ideal_client_key.sk_rlwe.values(),
&gadget_vec,
rlwe_q,
);
println!("####### ##################### #######");
}
}
if false {
let rlwe_q = bool_evaluator.parameters.rlwe_q; let rlwe_q = bool_evaluator.parameters.rlwe_q;
let rlwe_n = bool_evaluator.parameters.rlwe_n; let rlwe_n = bool_evaluator.parameters.rlwe_n;
let logp = 2;
let logp = 3;
let p = 1 << logp; let p = 1 << logp;
let rlwe_modop = &bool_evaluator.rlwe_modop; let rlwe_modop = &bool_evaluator.rlwe_modop;
let rlwe_nttop = &bool_evaluator.rlwe_nttop; let rlwe_nttop = &bool_evaluator.rlwe_nttop;
@ -1945,155 +1982,197 @@ mod tests {
&mut rng, &mut rng,
); );
// public_key_encrypt_rgsw(out_rgsw, m, public_key, gadget_vector, mod_op, // public_key_encrypt_rgsw(out_rgsw, m, public_key, gadget_vector, mod_op,
// ntt_op, rng)
// ntt_op, rng);
let mut rlwe_ct = let mut rlwe_ct =
RlweCiphertext::<Vec<Vec<u64>>, DefaultSecureRng>::from(&seeded_rlwe_ct); RlweCiphertext::<Vec<Vec<u64>>, DefaultSecureRng>::from(&seeded_rlwe_ct);
let index = 0;
for index in 0..200 {
// RLWE(m*X^{s[i]}) = RLWE(m) x RGSW(X^{s[i]})
let mut scratch_matrix_dplus2_ring = vec![vec![0u64; rlwe_n]; d_rgsw + 2];
let rlwe_decomposer = &bool_evaluator.decomposer_rlwe;
rlwe_by_rgsw(
&mut rlwe_ct,
server_key_eval.rgsw_ct_lwe_si(index),
// &rgsw_ct.data,
&mut scratch_matrix_dplus2_ring,
rlwe_decomposer,
rlwe_nttop,
rlwe_modop,
);
let mut rgsw_ct = {
let rgsw_seed = [0u8; 32];
let mut rgsw_prng = DefaultSecureRng::new_seeded(rgsw_seed);
let mut rgsw_ct = SeededRgswCiphertext::<Vec<Vec<u64>>, _>::empty(
rlwe_n, d_rgsw, rgsw_seed, rlwe_q,
// decrypt RLWE(m*X^{s[i]}) to get encoded m[X]*X^{s[i]}
let mut encoded_m_back = vec![0u64; rlwe_n];
decrypt_rlwe(
&rlwe_ct,
ideal_client_key.sk_rlwe.values(),
&mut encoded_m_back,
rlwe_nttop,
rlwe_modop,
); );
let m_back = encoded_m_back
.iter()
.map(|el| (((*el as f64 * p as f64) / (rlwe_q as f64)).round() as u64) % p)
.collect_vec();
// calculate m[X]X^{s[i]} in plain
let mut si_poly = vec![0u64; rlwe_n]; let mut si_poly = vec![0u64; rlwe_n];
// dbg!(ideal_client_key.sk_lwe.values()); // dbg!(ideal_client_key.sk_lwe.values());
let secret_el_i = ideal_client_key.sk_lwe.values[index]; let secret_el_i = ideal_client_key.sk_lwe.values[index];
dbg!(secret_el_i); dbg!(secret_el_i);
if secret_el_i < 0 { if secret_el_i < 0 {
si_poly[rlwe_n - secret_el_i.abs() as usize] = rlwe_q - 1;
si_poly[rlwe_n - secret_el_i.abs() as usize] = p - 1;
} else { } else {
si_poly[secret_el_i.abs() as usize] = 1; si_poly[secret_el_i.abs() as usize] = 1;
} }
secret_key_encrypt_rgsw(
&mut rgsw_ct.data,
&si_poly,
&gadget_vector(
bool_evaluator.parameters.rlwe_logq,
bool_evaluator.parameters.logb_rgsw,
d_rgsw,
),
ideal_client_key.sk_rlwe.values(),
rlwe_modop,
rlwe_nttop,
&mut rgsw_prng,
&mut rng,
);
let mul = |a: &u64, b: &u64| ((*a as u128 * *b as u128) % p as u128) as u64;
let expected_m = negacyclic_mul(&m, &si_poly, mul, p);
// measure noise
{
let encoded_m_ideal = expected_m
.iter()
.map(|el| ((*el as f64 * rlwe_q as f64) / (p as f64)).round() as u64)
.collect_vec();
let noise = measure_noise(
&rlwe_ct,
&encoded_m_ideal,
rlwe_nttop,
rlwe_modop,
ideal_client_key.sk_rlwe.values(),
);
println!("Noise RLWE(m X^s_{index}) = RLWE(m) x RGSW(X^s_{index}): {noise}")
}
// println!("M:{:?}", m);
// assert_eq!(expected_m, m_back);
RgswCiphertextEvaluationDomain::<_, DefaultSecureRng, NttBackendU64>::from(&rgsw_ct)
};
// println!("M_back:{:?} \n Expected_m:{:?}", m_back,
// expected_m);
}
}
}
// RLWE(m*X^{s[i]}) = RLWE(m) x RGSW(X^{s[i]})
let mut scratch_matrix_dplus2_ring = vec![vec![0u64; rlwe_n]; d_rgsw + 2];
let rlwe_decomposer = &bool_evaluator.decomposer_rlwe;
rlwe_by_rgsw(
&mut rlwe_ct,
server_key_eval.rgsw_ct_lwe_si(index),
// &rgsw_ct.data,
&mut scratch_matrix_dplus2_ring,
rlwe_decomposer,
rlwe_nttop,
rlwe_modop,
#[test]
fn trial12() {
let bool_evaluator =
BoolEvaluator::<Vec<Vec<u64>>, u64, NttBackendU64, ModularOpsU64>::new(MP_BOOL_PARAMS);
let no_of_parties = 2;
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,
);
// decrypt RLWE(m*X^{s[i]}) to get encoded m[X]*X^{s[i]}
let mut encoded_m_back = vec![0u64; rlwe_n];
decrypt_rlwe(
&rlwe_ct,
ideal_client_key.sk_rlwe.values(),
&mut encoded_m_back,
rlwe_nttop,
rlwe_modop,
// 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,
},
}
};
// PBS
let mut scratch_lwen_plus1 = vec![0u64; bool_evaluator.parameters.lwe_n + 1];
let mut scratch_matrix_dplus2_ring = vec![
vec![0u64; bool_evaluator.parameters.rlwe_n];
bool_evaluator.parameters.d_rgsw + 2
];
let mut m0 = true;
let mut m1 = false;
for _ in 0..100 {
let lwe0 = bool_evaluator.pk_encrypt(&collective_pk.key, m0);
let lwe1 = bool_evaluator.pk_encrypt(&collective_pk.key, m1);
let lwe_out = bool_evaluator.nand(
&lwe0,
&lwe1,
&server_key_eval,
&mut scratch_lwen_plus1,
&mut scratch_matrix_dplus2_ring,
); );
let m_back = encoded_m_back
.iter()
.map(|el| (((*el as f64 * p as f64) / (rlwe_q as f64)).round() as u64) % p)
.collect_vec();
// calculate m[X]X^{s[i]} in plain
let mut si_poly = vec![0u64; rlwe_n];
// dbg!(ideal_client_key.sk_lwe.values());
let secret_el_i = ideal_client_key.sk_lwe.values[index];
dbg!(secret_el_i);
if secret_el_i < 0 {
si_poly[rlwe_n - secret_el_i.abs() as usize] = p - 1;
} else {
si_poly[secret_el_i.abs() as usize] = 1;
let m_expected = !(m0 & m1);
// measure noise
{
// Trace PBS
PBSTracer::with_local(|t| {
t.trace(
&MP_BOOL_PARAMS,
&ideal_client_key.sk_lwe.values(),
&ideal_client_key.sk_rlwe.values(),
)
});
let ideal_m = if m_expected {
bool_evaluator.rlweq_by8
} else {
bool_evaluator.parameters.rlwe_q - bool_evaluator.rlweq_by8
};
let noise = measure_noise_lwe(
&lwe_out,
ideal_client_key.sk_rlwe.values(),
&bool_evaluator.rlwe_modop,
&ideal_m,
);
println!("Noise: {noise}");
} }
let mul = |a: &u64, b: &u64| ((*a as u128 * *b as u128) % p as u128) as u64;
let expected_m = negacyclic_mul(&m, &si_poly, mul, p);
assert_eq!(expected_m, m_back);
// println!("M:{:?}", m);
// println!("M_back:{:?} \n Expected_m:{:?}", m_back, expected_m);
}
// // PBS
// let mut scratch_lwen_plus1 = vec![0u64;
// bool_evaluator.parameters.lwe_n + 1];
// let mut scratch_matrix_dplus2_ring = vec![
// vec![0u64; bool_evaluator.parameters.rlwe_n];
// bool_evaluator.parameters.d_rgsw + 2
// ];
// let mut m0 = true;
// let mut m1 = false;
// for _ in 0..100 {
// let lwe0 = bool_evaluator.pk_encrypt(&collective_pk.key, m0);
// let lwe1 = bool_evaluator.pk_encrypt(&collective_pk.key, m1);
// let lwe_out = bool_evaluator.nand(
// &lwe0,
// &lwe1,
// &server_key_eval,
// &mut scratch_lwen_plus1,
// &mut scratch_matrix_dplus2_ring,
// );
// multi-party decrypt
let decryption_shares = parties
.iter()
.map(|k| bool_evaluator.multi_party_decryption_share(&lwe_out, k))
.collect_vec();
let m_back = bool_evaluator.multi_party_decrypt(&decryption_shares, &lwe_out);
let m_back = bool_evaluator.sk_decrypt(&lwe_out, &ideal_client_key);
// let m_expected = !(m0 & m1);
// // measure noise
// {
// // Trace PBS
// PBSTracer::with_local(|t| {
// t.trace(
// &MP_BOOL_PARAMS,
// &ideal_client_key.sk_lwe.values(),
// &ideal_client_key.sk_rlwe.values(),
// )
// });
// let ideal_m = if m_expected {
// bool_evaluator.rlweq_by8
// } else {
// bool_evaluator.parameters.rlwe_q -
// bool_evaluator.rlweq_by8 };
// let noise = measure_noise_lwe(
// &lwe_out,
// ideal_client_key.sk_rlwe.values(),
// &bool_evaluator.rlwe_modop,
// &ideal_m,
// );
// println!("Noise: {noise}");
// }
// // multi-party decrypt
// // let decryption_shares = parties
// // .iter()
// // .map(|k|
// bool_evaluator.multi_party_decryption_share(&lwe_out, k))
// // .collect_vec();
// // let m_back =
// bool_evaluator.multi_party_decrypt(&decryption_shares, //
// &lwe_out);
// let m_back = bool_evaluator.sk_decrypt(&lwe_out,
// &ideal_client_key);
// dbg!(m_expected, m_back);
// m1 = m0;
// m0 = m_back;
// }
dbg!(m_expected, m_back);
m1 = m0;
m0 = m_back;
}
} }
} }

+ 6
- 6
src/bool/parameters.rs

@ -28,7 +28,7 @@ pub(super) const SP_BOOL_PARAMS: BoolParameters = BoolParameters:: {
lwe_logq: 16, lwe_logq: 16,
br_q: 1 << 10, br_q: 1 << 10,
rlwe_n: 1 << 10, rlwe_n: 1 << 10,
lwe_n: 493,
lwe_n: 200,
d_rgsw: 3, d_rgsw: 3,
logb_rgsw: 8, logb_rgsw: 8,
d_lwe: 3, d_lwe: 3,
@ -38,15 +38,15 @@ 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: 2305843009213616129u64,
rlwe_logq: 61,
rlwe_q: 1152921504606830593,
rlwe_logq: 60,
lwe_q: 1 << 25, lwe_q: 1 << 25,
lwe_logq: 25, lwe_logq: 25,
br_q: 1 << 11, br_q: 1 << 11,
rlwe_n: 1 << 11, rlwe_n: 1 << 11,
lwe_n: 500, lwe_n: 500,
d_rgsw: 7,
logb_rgsw: 8,
d_rgsw: 10,
logb_rgsw: 6,
d_lwe: 5, d_lwe: 5,
logb_lwe: 5, logb_lwe: 5,
g: 5, g: 5,
@ -59,7 +59,7 @@ mod tests {
#[test] #[test]
fn find_prime() { fn find_prime() {
let bits = 61;
let bits = 60;
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);

+ 14
- 9
src/decomposer.rs

@ -90,9 +90,9 @@ impl Decomposer for DefaultDecomposer {
let mut value = round_value(*value, self.ignore_bits); let mut value = round_value(*value, self.ignore_bits);
let q = self.q; let q = self.q;
if value >= (q >> 1) {
value = value.wrapping_sub(&q);
}
// if value >= (q >> 1) {
// value = value.wrapping_sub(&q);
// }
let logb = self.logb; let logb = self.logb;
let b = T::one() << logb; // base let b = T::one() << logb; // base
@ -105,7 +105,7 @@ impl Decomposer for DefaultDecomposer {
for i in 0..self.d { for i in 0..self.d {
let mut limb = ((value >> (logb * i)) & full_mask) + carry; let mut limb = ((value >> (logb * i)) & full_mask) + carry;
carry = T::zero(); carry = T::zero();
if limb > b_by2 {
if limb >= b_by2 {
limb = (q + limb) - b; limb = (q + limb) - b;
carry = T::one(); carry = T::one();
} }
@ -122,6 +122,11 @@ impl Decomposer for DefaultDecomposer {
// carry = carry >> (logb - 1); // carry = carry >> (logb - 1);
} }
out[self.d - 1] = out[self.d - 1] + (carry << logb);
if out[self.d - 1] > q {
out[self.d - 1] = out[self.d - 1] - q;
}
return out; return out;
} }
@ -153,15 +158,15 @@ mod tests {
#[test] #[test]
fn decomposition_works() { fn decomposition_works() {
let logq = 15;
let logb = 3;
let d = 5;
let logq = 60;
let logb = 5;
let d = 12;
let mut rng = thread_rng(); let mut rng = thread_rng();
// q is prime of bits logq and i is true, other q = 1<<logq // q is prime of bits logq and i is true, other q = 1<<logq
// FIXME: Test fails when q is prime, albeit the difference is minute // FIXME: Test fails when q is prime, albeit the difference is minute
for i in [true] {
for i in [true, false] {
let q = if i { let q = if i {
generate_prime(logq, 1 << 4, 1u64 << logq).unwrap() generate_prime(logq, 1 << 4, 1u64 << logq).unwrap()
} else { } else {
@ -175,7 +180,7 @@ mod tests {
let value_back = decomposer.recompose(&limbs, &modq_op); let value_back = decomposer.recompose(&limbs, &modq_op);
let rounded_value = let rounded_value =
round_value(value, decomposer.ignore_bits) << decomposer.ignore_bits; round_value(value, decomposer.ignore_bits) << decomposer.ignore_bits;
// dbg!(rounded_value, value, value_back);
// dbg!(&limbs, q);
assert_eq!( assert_eq!(
rounded_value, value_back, rounded_value, value_back,
"Expected {rounded_value} got {value_back} for q={q}" "Expected {rounded_value} got {value_back} for q={q}"

+ 274
- 85
src/rgsw.rs

@ -859,14 +859,20 @@ pub(crate) fn rgsw_by_rgsw_inplace<
// RGSW x RGSW // RGSW x RGSW
izip!( izip!(
rgsw1_nsm.iter().take(d_rgsw).chain(rgsw1_m).take(d_rgsw),
rgsw1_nsm.iter().skip(d_rgsw).chain(rgsw1_m).skip(d_rgsw),
rgsw1_nsm
.iter()
.take(d_rgsw)
.chain(rgsw1_m.iter().take(d_rgsw)),
rgsw1_nsm
.iter()
.skip(d_rgsw)
.chain(rgsw1_m.iter().skip(d_rgsw)),
rlwe_dash_space_nsm_parta rlwe_dash_space_nsm_parta
.iter_mut() .iter_mut()
.chain(rlwe_dash_space_m_parta),
.chain(rlwe_dash_space_m_parta.iter_mut()),
rlwe_dash_space_nsm_partb rlwe_dash_space_nsm_partb
.iter_mut() .iter_mut()
.chain(rlwe_dash_space_m_partb),
.chain(rlwe_dash_space_m_partb.iter_mut()),
) )
.for_each(|(rlwe_a, rlwe_b, rlwe_out_a, rlwe_out_b)| { .for_each(|(rlwe_a, rlwe_b, rlwe_out_a, rlwe_out_b)| {
// Part A // Part A
@ -928,7 +934,7 @@ pub(crate) fn secret_key_encrypt_rgsw<
NttOp: Ntt<Element = Mmut::MatElement>, NttOp: Ntt<Element = Mmut::MatElement>,
>( >(
out_rgsw: &mut Mmut, out_rgsw: &mut Mmut,
m: &Mmut::R,
m: &[Mmut::MatElement],
gadget_vector: &[Mmut::MatElement], gadget_vector: &[Mmut::MatElement],
s: &[S], s: &[S],
mod_op: &ModOp, mod_op: &ModOp,
@ -1300,7 +1306,7 @@ where
let mut max_diff_bits = f64::MIN; let mut max_diff_bits = f64::MIN;
m_plus_e.as_ref().iter().for_each(|v| { m_plus_e.as_ref().iter().for_each(|v| {
let mut v = *v; let mut v = *v;
println!("{:?}", v);
if v >= (q >> 1) { if v >= (q >> 1) {
// v is -ve // v is -ve
v = q - v; v = q - v;
@ -1317,14 +1323,14 @@ where
} }
#[cfg(test)] #[cfg(test)]
mod tests {
pub(crate) mod tests {
use std::{ops::Mul, vec}; use std::{ops::Mul, vec};
use itertools::{izip, Itertools}; use itertools::{izip, Itertools};
use rand::{thread_rng, Rng}; use rand::{thread_rng, Rng};
use crate::{ use crate::{
backend::{ArithmeticOps, ModInit, ModularOpsU64},
backend::{ModInit, ModularOpsU64, VectorOps},
decomposer::{gadget_vector, DefaultDecomposer}, decomposer::{gadget_vector, DefaultDecomposer},
ntt::{self, Ntt, NttBackendU64, NttInit}, ntt::{self, Ntt, NttBackendU64, NttInit},
random::{DefaultSecureRng, NewWithSeed, RandomUniformDist}, random::{DefaultSecureRng, NewWithSeed, RandomUniformDist},
@ -1333,7 +1339,7 @@ mod tests {
RgswCiphertext, RgswCiphertextEvaluationDomain, RlweCiphertext, RlwePublicKey, RgswCiphertext, RgswCiphertextEvaluationDomain, RlweCiphertext, RlwePublicKey,
SeededAutoKey, SeededRgswCiphertext, SeededRlweCiphertext, SeededRlwePublicKey, SeededAutoKey, SeededRgswCiphertext, SeededRlweCiphertext, SeededRlwePublicKey,
}, },
utils::{generate_prime, negacyclic_mul},
utils::{generate_prime, negacyclic_mul, TryConvertFrom},
Matrix, Secret, Matrix, Secret,
}; };
@ -1405,7 +1411,7 @@ mod tests {
let ring_size = 1 << 9; let ring_size = 1 << 9;
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 = 9;
let d_rgsw = 10;
let logb = 5; let logb = 5;
let mut rng = DefaultSecureRng::new_seeded([0u8; 32]); let mut rng = DefaultSecureRng::new_seeded([0u8; 32]);
@ -1419,39 +1425,18 @@ mod tests {
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);
let gadget_vector = gadget_vector(logq, logb, d_rgsw);
// Encrypt m1 as RGSW(m1) // Encrypt m1 as RGSW(m1)
let rgsw_ct = { let rgsw_ct = {
//TODO(Jay): Figure out better way to test secret key and public key variant of //TODO(Jay): Figure out better way to test secret key and public key variant of
// RGSW ciphertext encryption within the same test // RGSW ciphertext encryption within the same test
if false {
// RGSW(m1) encryption using secret key
let mut rgsw_seed = [0u8; 32];
rng.fill_bytes(&mut rgsw_seed);
let mut seeded_rgsw_ct = SeededRgswCiphertext::<Vec<Vec<u64>>, [u8; 32]>::empty(
ring_size as usize,
d_rgsw,
rgsw_seed,
q,
);
let mut p_rng = DefaultSecureRng::new_seeded(rgsw_seed);
let gadget_vector = gadget_vector(logq, logb, d_rgsw);
secret_key_encrypt_rgsw(
&mut seeded_rgsw_ct.data,
&m1,
&gadget_vector,
s.values(),
&mod_op,
&ntt_op,
&mut p_rng,
&mut rng,
);
RgswCiphertextEvaluationDomain::<_, DefaultSecureRng, NttBackendU64>::from(
&seeded_rgsw_ct,
)
if true {
// Encryption m1 as RGSW(m1) using secret key
_sk_encrypt_rgsw(&m1, s.values(), &gadget_vector, &mod_op, &ntt_op)
} else { } else {
// RGSW(m1) encryption using public key
// Encrypt m1 as RGSW(m1) as public key
// first create public key // first create public key
let mut pk_seed = [0u8; 32]; let mut pk_seed = [0u8; 32];
@ -1469,22 +1454,10 @@ mod tests {
); );
let pk = RlwePublicKey::<Vec<Vec<u64>>, DefaultSecureRng>::from(&seeded_pk); let pk = RlwePublicKey::<Vec<Vec<u64>>, DefaultSecureRng>::from(&seeded_pk);
// public key encrypt RGSW(m1)
let mut rgsw_ct = vec![vec![0u64; ring_size as usize]; d_rgsw * 4];
let gadget_vector = gadget_vector(logq, logb, d_rgsw);
public_key_encrypt_rgsw(
&mut rgsw_ct,
&m1,
&pk.data,
&gadget_vector,
&mod_op,
&ntt_op,
&mut rng,
);
let rgsw_ct = _pk_encrypt_rgsw(&m1, &pk, &gadget_vector, &mod_op, &ntt_op);
RgswCiphertextEvaluationDomain::<_, DefaultSecureRng, NttBackendU64>::from( RgswCiphertextEvaluationDomain::<_, DefaultSecureRng, NttBackendU64>::from(
&RgswCiphertext { &RgswCiphertext {
data: rgsw_ct,
data: rgsw_ct.data,
modulus: q, modulus: q,
}, },
) )
@ -1543,6 +1516,18 @@ mod tests {
let mul_mod = |v0: &u64, v1: &u64| (v0 * v1) % p; let mul_mod = |v0: &u64, v1: &u64| (v0 * v1) % p;
let m0m1 = negacyclic_mul(&m0, &m1, mul_mod, p); let m0m1 = negacyclic_mul(&m0, &m1, mul_mod, p);
{
// measure noise
let encoded_m_ideal = m0m1
.iter()
.map(|v| (((*v as f64) * q as f64) / (p as f64)).round() as u64)
.collect_vec();
let noise = measure_noise(&rlwe_in_ct, &encoded_m_ideal, &ntt_op, &mod_op, s.values());
println!("Noise RLWE(m0m1)(= RLWE(m0)xRGSW(m1)) : {noise}");
}
assert!( assert!(
m0m1 == m0m1_back, m0m1 == m0m1_back,
"Expected {:?} \n Got {:?}", "Expected {:?} \n Got {:?}",
@ -1551,6 +1536,52 @@ mod tests {
); );
} }
#[test]
fn rlwe_by_rgsw_noise_growth() {
let logq = 31;
let ring_size = 1 << 9;
let q = generate_prime(logq, ring_size * 2, 1u64 << logq).unwrap();
let d_rgsw = 6;
let logb = 5;
let s = RlweSecret::random((ring_size >> 1) as usize, ring_size as usize);
let ntt_op = NttBackendU64::new(q, ring_size as usize);
let mod_op = ModularOpsU64::new(q);
let gadget_vector = gadget_vector(logq, logb, d_rgsw);
let decomposer = DefaultDecomposer::new(q, logb, d_rgsw);
let mul_mod = |v0: &u64, v1: &u64| ((*v0 as u128 * *v1 as u128) % (q as u128)) as u64;
let mut carry_m = vec![0u64; ring_size as usize];
carry_m[thread_rng().gen_range(0..ring_size) as usize] = 1;
let mut rlwe = vec![vec![0u64; ring_size as usize], carry_m.clone()];
let mut rlwe = RlweCiphertext::<Vec<Vec<u64>>, DefaultSecureRng>::from_raw(rlwe, true);
let mut scratch_matrix_dplus2_ring = vec![vec![0u64; ring_size as usize]; d_rgsw + 2];
for i in 0..1usize {
// Encrypt monomial as RGSW
let mut m = vec![0u64; ring_size as usize];
m[thread_rng().gen_range(0..ring_size) as usize] = if i & 1 == 1 { 1 } else { q - 1 };
let rgsw_ct = _sk_encrypt_rgsw(&m, s.values(), &gadget_vector, &mod_op, &ntt_op);
// RLWE(carry_m * m) = RLWE(carry_m) x RGSW(m)
rlwe_by_rgsw(
&mut rlwe,
&rgsw_ct.data,
&mut scratch_matrix_dplus2_ring,
&decomposer,
&ntt_op,
&mod_op,
);
carry_m = negacyclic_mul(&carry_m, &m, mul_mod, q);
let noise = measure_noise(&rlwe, &carry_m, &ntt_op, &mod_op, s.values());
println!("Noise RLWE(carry_m) after {i}^th iteration: {noise}");
}
}
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>,
@ -1583,15 +1614,112 @@ mod tests {
} }
} }
/// Encrypts m as RGSW ciphertext RGSW(m) using supplied secret key. Returns
/// unseeded RGSW ciphertext in coefficient domain
fn _sk_encrypt_rgsw(
m: &[u64],
s: &[i32],
gadget_vector: &[u64],
mod_op: &ModularOpsU64,
ntt_op: &NttBackendU64,
) -> RgswCiphertextEvaluationDomain<Vec<Vec<u64>>, DefaultSecureRng, NttBackendU64> {
let ring_size = s.len();
assert!(m.len() == s.len());
let d_rgsw = gadget_vector.len();
let q = mod_op.modulus();
let mut rng = DefaultSecureRng::new();
let mut rgsw_seed = [0u8; 32];
rng.fill_bytes(&mut rgsw_seed);
let mut seeded_rgsw_ct = SeededRgswCiphertext::<Vec<Vec<u64>>, [u8; 32]>::empty(
ring_size as usize,
d_rgsw,
rgsw_seed,
q,
);
let mut p_rng = DefaultSecureRng::new_seeded(rgsw_seed);
secret_key_encrypt_rgsw(
&mut seeded_rgsw_ct.data,
m,
&gadget_vector,
s,
mod_op,
ntt_op,
&mut p_rng,
&mut rng,
);
RgswCiphertextEvaluationDomain::<_, DefaultSecureRng, NttBackendU64>::from(&seeded_rgsw_ct)
}
/// Prints noise in RGSW ciphertext RGSW(m).
///
/// - rgsw_ct: RGSW ciphertext in coefficient domain
pub(crate) fn _measure_noise_rgsw(
rgsw_ct: &[Vec<u64>],
m: &[u64],
s: &[i32],
gadget_vector: &[u64],
q: u64,
) {
let d_rgsw = gadget_vector.len();
let ring_size = s.len();
assert!(Matrix::dimension(&rgsw_ct) == (d_rgsw * 2 * 2, ring_size));
assert!(m.len() == ring_size);
let mod_op = ModularOpsU64::new(q);
let ntt_op = NttBackendU64::new(q, ring_size);
let mul_mod = |a: &u64, b: &u64| ((*a as u128 * *b as u128) % q as u128) as u64;
let s_poly = Vec::<u64>::try_convert_from(s, &q);
let mut neg_s = s_poly.clone();
mod_op.elwise_neg_mut(neg_s.as_mut());
let neg_sm0m1 = negacyclic_mul(&neg_s, &m, mul_mod, q);
for i in 0..2 {
for j in 0..d_rgsw {
let ideal_m = {
if i == 0 {
// RLWE(\beta^j -s * m)
let mut beta_neg_sm0m1 = vec![0u64; ring_size as usize];
mod_op.elwise_scalar_mul(
beta_neg_sm0m1.as_mut(),
&neg_sm0m1,
&gadget_vector[j],
);
beta_neg_sm0m1
} else {
// RLWE(\beta^j m)
let mut beta_m0m1 = vec![0u64; ring_size as usize];
mod_op.elwise_scalar_mul(beta_m0m1.as_mut(), &m, &gadget_vector[j]);
beta_m0m1
}
};
let mut rlwe = vec![vec![0u64; ring_size as usize]; 2];
rlwe[0].copy_from_slice(rgsw_ct.get_row_slice((i * 2 * d_rgsw) + j));
rlwe[1].copy_from_slice(rgsw_ct.get_row_slice((i * 2 * d_rgsw) + d_rgsw + j));
let noise = measure_noise(&rlwe, &ideal_m, &ntt_op, &mod_op, s);
if i == 0 {
println!(r"Noise RLWE(\beta^{j} -sm0m1): {noise}");
} else {
println!(r"Noise RLWE(\beta^{j} m0m1): {noise}");
}
}
// m0m1
}
}
#[test] #[test]
fn rgsw_by_rgsw() {
let logq = 50;
fn pk_rgsw_by_rgsw() {
let logq = 60;
let logp = 2; let logp = 2;
let ring_size = 1 << 4;
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 = 10;
let logb = 5;
let d_rgsw = 15;
let logb = 4;
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);
@ -1619,43 +1747,104 @@ mod tests {
RlwePublicKey::<Vec<Vec<u64>>, DefaultSecureRng>::from(&seeded_pk) RlwePublicKey::<Vec<Vec<u64>>, DefaultSecureRng>::from(&seeded_pk)
}; };
let mut m0 = vec![0u64; ring_size as usize];
m0[thread_rng().gen_range(0..ring_size) as usize] = 1;
let mut m1 = vec![0u64; ring_size as usize];
m1[thread_rng().gen_range(0..ring_size) as usize] = 1;
// RGSW(m0)
let rgsw_m0 = _pk_encrypt_rgsw(&m0, &public_key, &gadget_vector, &mod_op, &ntt_op);
// RGSW(m1)
let rgsw_m1 = _pk_encrypt_rgsw(&m0, &public_key, &gadget_vector, &mod_op, &ntt_op);
let mut carry_m = vec![0u64; ring_size as usize];
carry_m[thread_rng().gen_range(0..ring_size) as usize] = 1;
let mut rgsw_m0_eval =
RgswCiphertextEvaluationDomain::<_, DefaultSecureRng, NttBackendU64>::from(&rgsw_m0);
// RGSW(carry_m)
let rgsw_carrym = _pk_encrypt_rgsw(&carry_m, &public_key, &gadget_vector, &mod_op, &ntt_op);
let mut rgsw_carrym =
RgswCiphertextEvaluationDomain::<_, DefaultSecureRng, NttBackendU64>::from(
&rgsw_carrym,
);
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)];
rgsw_by_rgsw_inplace(
&mut rgsw_m0_eval.data,
&rgsw_m1.data,
&decomposer,
&mut scratch_matrix_d_plus_rgsw_by_ring,
&ntt_op,
&mod_op,
);
dbg!(&rgsw_m0_eval.data);
// RLWE(m0m1)
let mut rlwe_m0m1 = vec![vec![0u64; ring_size as usize]; 2];
rlwe_m0m1[0].copy_from_slice(rgsw_m0_eval.get_row_slice(2 * d_rgsw));
rlwe_m0m1[1].copy_from_slice(rgsw_m0_eval.get_row_slice(3 * d_rgsw));
rlwe_m0m1.iter_mut().for_each(|ri| ntt_op.backward(ri));
for i in 0..10 {
let mut m = vec![0u64; ring_size as usize];
m[thread_rng().gen_range(0..ring_size) as usize] = q - 1;
let rgsw_m = _pk_encrypt_rgsw(&m, &public_key, &gadget_vector, &mod_op, &ntt_op);
// m0m1
let mul = |a: &u64, b: &u64| ((*a as u128 * *b as u128) % q as u128) as u64;
let m0m1 = negacyclic_mul(&m0, &m1, mul, q);
rgsw_by_rgsw_inplace(
&mut rgsw_carrym.data,
&rgsw_m.data,
&decomposer,
&mut scratch_matrix_d_plus_rgsw_by_ring,
&ntt_op,
&mod_op,
);
// 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);
println!("########### Noise RGSW(carrym) in {i}^th loop ###########");
let mut rgsw_carrym_clone = rgsw_carrym.data.clone();
rgsw_carrym_clone
.iter_mut()
.for_each(|ri| ntt_op.backward(ri.as_mut()));
_measure_noise_rgsw(&rgsw_carrym_clone, &carry_m, s.values(), &gadget_vector, q);
}
}
let noise = measure_noise(&rlwe_m0m1, &m0m1, &ntt_op, &mod_op, s.values());
dbg!(noise);
#[test]
fn sk_rgsw_by_rgsw() {
let logq = 60;
let logp = 2;
let ring_size = 1 << 11;
let q = generate_prime(logq, ring_size, 1u64 << logq).unwrap();
let p = 1u64 << logp;
let d_rgsw = 15;
let logb = 4;
let s = RlweSecret::random((ring_size >> 1) as usize, ring_size as usize);
let mut rng = DefaultSecureRng::new();
let ntt_op = NttBackendU64::new(q, ring_size as usize);
let mod_op = ModularOpsU64::new(q);
let gadget_vector = gadget_vector(logq, logb, d_rgsw);
let decomposer = DefaultDecomposer::new(q, logb, d_rgsw);
let mut carry_m = vec![0u64; ring_size as usize];
carry_m[thread_rng().gen_range(0..ring_size) as usize] = 1;
// RGSW(carry_m)
let mut rgsw_carrym =
_sk_encrypt_rgsw(&carry_m, s.values(), &gadget_vector, &mod_op, &ntt_op);
let mut scratch_matrix_d_plus_rgsw_by_ring =
vec![vec![0u64; ring_size as usize]; d_rgsw + (d_rgsw * 4)];
for i in 0..1 {
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 };
let rgsw_m = {
let mut rgsw_eval =
_sk_encrypt_rgsw(&m, s.values(), &gadget_vector, &mod_op, &ntt_op).data;
rgsw_eval
.iter_mut()
.for_each(|ri| ntt_op.backward(ri.as_mut()));
rgsw_eval
};
rgsw_by_rgsw_inplace(
&mut rgsw_carrym.data,
&rgsw_m,
&decomposer,
&mut scratch_matrix_d_plus_rgsw_by_ring,
&ntt_op,
&mod_op,
);
// 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);
println!("########### Noise RGSW(carrym) in {i}^th loop ###########");
let mut rgsw_carrym_clone = rgsw_carrym.data.clone();
rgsw_carrym_clone
.iter_mut()
.for_each(|ri| ntt_op.backward(ri.as_mut()));
_measure_noise_rgsw(&rgsw_carrym_clone, &carry_m, s.values(), &gadget_vector, q);
}
} }
#[test] #[test]

Loading…
Cancel
Save