From b4519b749648fbe966b5e76b81df1d9b8eeed05d Mon Sep 17 00:00:00 2001 From: Janmajaya Mall Date: Sat, 4 May 2024 15:44:11 +0530 Subject: [PATCH] RLWExRGSW error grwoth behaves weird for primes > 30 bits --- src/bool/evaluator.rs | 433 ++++++++++++++++++++++++----------------- src/bool/parameters.rs | 12 +- src/decomposer.rs | 23 ++- src/rgsw.rs | 359 ++++++++++++++++++++++++++-------- 4 files changed, 550 insertions(+), 277 deletions(-) diff --git a/src/bool/evaluator.rs b/src/bool/evaluator.rs index e3938d7..51044ab 100644 --- a/src/bool/evaluator.rs +++ b/src/bool/evaluator.rs @@ -756,7 +756,7 @@ where let mut rgsw_si = M::zeros(self.parameters.d_rgsw * 3, ring_size); secret_key_encrypt_rgsw( &mut rgsw_si, - &m, + m.as_ref(), &d_rgsw_gadget_vec, sk_rlwe.values(), &self.rlwe_modop, @@ -1635,8 +1635,8 @@ mod tests { ntt::NttBackendU64, random::DEFAULT_RNG, 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, }; @@ -1803,11 +1803,11 @@ mod tests { } #[test] - fn trial_mp() { + fn mp_key_correcntess() { let bool_evaluator = BoolEvaluator::>, u64, NttBackendU64, ModularOpsU64>::new(MP_BOOL_PARAMS); - let no_of_parties = 2; + let no_of_parties = 5; let parties = (0..no_of_parties) .map(|_| bool_evaluator.client_key()) .collect_vec(); @@ -1863,58 +1863,95 @@ mod tests { }; // 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_n = bool_evaluator.parameters.rlwe_n; - let logp = 2; + let logp = 3; let p = 1 << logp; let rlwe_modop = &bool_evaluator.rlwe_modop; let rlwe_nttop = &bool_evaluator.rlwe_nttop; @@ -1945,155 +1982,197 @@ mod tests { &mut rng, ); // public_key_encrypt_rgsw(out_rgsw, m, public_key, gadget_vector, mod_op, - // ntt_op, rng) + // ntt_op, rng); let mut rlwe_ct = RlweCiphertext::>, 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::>, _>::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]; // 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] = rlwe_q - 1; + si_poly[rlwe_n - secret_el_i.abs() as usize] = p - 1; } else { 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::>, 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::>, 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; + } } } diff --git a/src/bool/parameters.rs b/src/bool/parameters.rs index 6c6556c..f8698dc 100644 --- a/src/bool/parameters.rs +++ b/src/bool/parameters.rs @@ -28,7 +28,7 @@ pub(super) const SP_BOOL_PARAMS: BoolParameters = BoolParameters:: { lwe_logq: 16, br_q: 1 << 10, rlwe_n: 1 << 10, - lwe_n: 493, + lwe_n: 200, d_rgsw: 3, logb_rgsw: 8, d_lwe: 3, @@ -38,15 +38,15 @@ pub(super) const SP_BOOL_PARAMS: BoolParameters = BoolParameters:: { }; pub(super) const MP_BOOL_PARAMS: BoolParameters = BoolParameters:: { - rlwe_q: 2305843009213616129u64, - rlwe_logq: 61, + rlwe_q: 1152921504606830593, + rlwe_logq: 60, lwe_q: 1 << 25, lwe_logq: 25, br_q: 1 << 11, rlwe_n: 1 << 11, lwe_n: 500, - d_rgsw: 7, - logb_rgsw: 8, + d_rgsw: 10, + logb_rgsw: 6, d_lwe: 5, logb_lwe: 5, g: 5, @@ -59,7 +59,7 @@ mod tests { #[test] fn find_prime() { - let bits = 61; + let bits = 60; let ring_size = 1 << 11; let prime = generate_prime(bits, ring_size * 2, 1 << bits).unwrap(); dbg!(prime); diff --git a/src/decomposer.rs b/src/decomposer.rs index 8ff5b6b..d1044ab 100644 --- a/src/decomposer.rs +++ b/src/decomposer.rs @@ -90,9 +90,9 @@ impl Decomposer for DefaultDecomposer { let mut value = round_value(*value, self.ignore_bits); 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 b = T::one() << logb; // base @@ -105,7 +105,7 @@ impl Decomposer for DefaultDecomposer { for i in 0..self.d { let mut limb = ((value >> (logb * i)) & full_mask) + carry; carry = T::zero(); - if limb > b_by2 { + if limb >= b_by2 { limb = (q + limb) - b; carry = T::one(); } @@ -122,6 +122,11 @@ impl Decomposer for DefaultDecomposer { // 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; } @@ -153,15 +158,15 @@ mod tests { #[test] 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(); // q is prime of bits logq and i is true, other q = 1<, >( out_rgsw: &mut Mmut, - m: &Mmut::R, + m: &[Mmut::MatElement], gadget_vector: &[Mmut::MatElement], s: &[S], mod_op: &ModOp, @@ -1300,7 +1306,7 @@ where let mut max_diff_bits = f64::MIN; m_plus_e.as_ref().iter().for_each(|v| { let mut v = *v; - + println!("{:?}", v); if v >= (q >> 1) { // v is -ve v = q - v; @@ -1317,14 +1323,14 @@ where } #[cfg(test)] -mod tests { +pub(crate) mod tests { use std::{ops::Mul, vec}; use itertools::{izip, Itertools}; use rand::{thread_rng, Rng}; use crate::{ - backend::{ArithmeticOps, ModInit, ModularOpsU64}, + backend::{ModInit, ModularOpsU64, VectorOps}, decomposer::{gadget_vector, DefaultDecomposer}, ntt::{self, Ntt, NttBackendU64, NttInit}, random::{DefaultSecureRng, NewWithSeed, RandomUniformDist}, @@ -1333,7 +1339,7 @@ mod tests { RgswCiphertext, RgswCiphertextEvaluationDomain, RlweCiphertext, RlwePublicKey, SeededAutoKey, SeededRgswCiphertext, SeededRlweCiphertext, SeededRlwePublicKey, }, - utils::{generate_prime, negacyclic_mul}, + utils::{generate_prime, negacyclic_mul, TryConvertFrom}, Matrix, Secret, }; @@ -1405,7 +1411,7 @@ mod tests { let ring_size = 1 << 9; let q = generate_prime(logq, ring_size, 1u64 << logq).unwrap(); let p = 1u64 << logp; - let d_rgsw = 9; + let d_rgsw = 10; let logb = 5; 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 mod_op = ModularOpsU64::new(q); + let gadget_vector = gadget_vector(logq, logb, d_rgsw); // Encrypt m1 as RGSW(m1) let rgsw_ct = { //TODO(Jay): Figure out better way to test secret key and public key variant of // 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::>, [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 { - // RGSW(m1) encryption using public key + // Encrypt m1 as RGSW(m1) as public key // first create public key let mut pk_seed = [0u8; 32]; @@ -1469,22 +1454,10 @@ mod tests { ); let pk = RlwePublicKey::>, 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( &RgswCiphertext { - data: rgsw_ct, + data: rgsw_ct.data, modulus: q, }, ) @@ -1543,6 +1516,18 @@ mod tests { let mul_mod = |v0: &u64, v1: &u64| (v0 * v1) % 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!( m0m1 == m0m1_back, "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::>, 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( m: &[u64], public_key: &RlwePublicKey>, 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>, 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::>, [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], + 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::::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] - fn rgsw_by_rgsw() { - let logq = 50; + fn pk_rgsw_by_rgsw() { + let logq = 60; let logp = 2; - let ring_size = 1 << 4; + let ring_size = 1 << 11; let q = generate_prime(logq, ring_size, 1u64 << logq).unwrap(); 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); @@ -1619,43 +1747,104 @@ mod tests { RlwePublicKey::>, 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 = 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]