diff --git a/src/bool/evaluator.rs b/src/bool/evaluator.rs index c3fb8f1..c20f01f 100644 --- a/src/bool/evaluator.rs +++ b/src/bool/evaluator.rs @@ -2,9 +2,7 @@ use std::{ cell::RefCell, collections::HashMap, fmt::{Debug, Display}, - hash::Hash, marker::PhantomData, - thread::panicking, }; use itertools::{izip, partition, Itertools}; @@ -45,6 +43,7 @@ trait PbsKey { /// LWE ksk to key switch from RLWE secret to LWE secret fn lwe_ksk(&self) -> &Self::M; } + trait PbsParameters { type Element; type D: Decomposer; @@ -570,6 +569,7 @@ where embedding_factor: usize, nand_test_vec: M::R, rlweq_by8: M::MatElement, + rlwe_qby4: M::MatElement, rlwe_auto_maps: Vec<(Vec, Vec)>, _phantom: PhantomData, } @@ -582,7 +582,7 @@ where + VectorOps, M::MatElement: PrimInt + Debug + Display + NumInfo + FromPrimitive + WrappingSub, 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>, ::R: RowMut, DefaultSecureRng: RandomGaussianDist<[M::MatElement], Parameters = M::MatElement> @@ -630,7 +630,6 @@ where let q = parameters.br_q; let qby2 = q >> 1; let qby8 = q >> 3; - let qby16 = q >> 4; let mut nand_test_vec = M::R::zeros(qby2); // Q/8 (Q: rlwe_q) let rlwe_qby8 = @@ -646,14 +645,14 @@ where 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}) 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)) } + let rlwe_qby4 = + M::MatElement::from_f64((parameters.rlwe_q.to_f64().unwrap() / 4.0).round()).unwrap(); + BoolEvaluator { parameters: parameters, decomposer_lwe, @@ -691,6 +693,7 @@ where rlwe_nttop, nand_test_vec: nand_test_vec_autog, rlweq_by8: rlwe_qby8, + rlwe_qby4: rlwe_qby4, rlwe_auto_maps, _phantom: PhantomData, @@ -1116,8 +1119,11 @@ where // ); // let c1_noise = // 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 modop = &self.rlwe_modop; @@ -1130,7 +1136,7 @@ where *o = modop.add(i0, i1); }); // +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| { // let noise = measure_noise_lwe( @@ -1364,6 +1370,15 @@ fn pbs< let br_qf64 = br_q.to_f64().unwrap(); 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 lwe_in.as_mut().iter_mut().for_each(|v| { *v = @@ -1590,6 +1605,7 @@ struct PBSTracer where 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_after_ksk: M::R, pub(crate) ct_br_q_mod: Vec, @@ -1600,21 +1616,26 @@ impl PBSTracer>> { assert!(parameters.rlwe_n == sk_rlwe.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); // 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 - 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 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!( " - 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)] mod tests { use rand::{thread_rng, Rng}; + use rand_distr::Uniform; use crate::{ backend::ModularOpsU64, @@ -1708,6 +1730,7 @@ mod tests { let mut m1 = true; let mut ct0 = bool_evaluator.sk_encrypt(m0, &client_key); let mut ct1 = bool_evaluator.sk_encrypt(m1, &client_key); + for _ in 0..1000 { let ct_back = bool_evaluator.nand( &ct0, @@ -1721,6 +1744,45 @@ mod tests { // 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 PBSTracer::with_local(|t| { t.trace( @@ -1731,21 +1793,32 @@ mod tests { }); // 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); - assert_eq!(m_out, m_back); + assert!(m_out == m_back, "Expected {m_out}, got {m_back}"); println!("----------"); m1 = m0; @@ -1761,7 +1834,7 @@ mod tests { let bool_evaluator = BoolEvaluator::>, u64, NttBackendU64, ModularOpsU64>::new(MP_BOOL_PARAMS); - let no_of_parties = 5; + let no_of_parties = 500; let parties = (0..no_of_parties) .map(|_| bool_evaluator.client_key()) .collect_vec(); @@ -1773,6 +1846,8 @@ mod tests { }); }); + println!("{:?}", &ideal_rlwe_sk); + let mut m = true; for i in 0..100 { 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] fn multi_party_lwe_keyswitch() { let lwe_logq = 18; 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 lweq_modop = ModularOpsU64::new(lwe_q); let logp = 2; let from_lwe_n = 2048; - let to_lwe_n = 583; + let to_lwe_n = 500; let no_of_parties = 10; 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)) .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 let mut rng = DefaultSecureRng::new(); let mut ksk_seed = [0u8; 32]; 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 p_rng = DefaultSecureRng::new_seeded(ksk_seed); lwe_ksk_keygen( @@ -1853,11 +1992,12 @@ mod tests { &mut rng, ); ksk_out - }); + }) + .collect_vec(); // Create collective LWE ksk 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()) }); 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; }); - // 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> { @@ -1966,7 +2069,7 @@ mod tests { vec![pk_part_a, pk_part_b] } - fn _multi_party_keygen( + fn _multi_party_all_keygen( bool_evaluator: &BoolEvaluator>, u64, NttBackendU64, ModularOpsU64>, no_of_parties: usize, ) -> ( @@ -2053,7 +2156,7 @@ mod tests { BoolEvaluator::>, u64, NttBackendU64, ModularOpsU64>::new(MP_BOOL_PARAMS); 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 rlwe_q = bool_evaluator.parameters.rlwe_q; @@ -2067,7 +2170,7 @@ mod tests { let rlwe_decomposer = &bool_evaluator.decomposer_rlwe; // test LWE ksk from RLWE -> LWE - if true { + if false { let logp = 2; let mut rng = DefaultSecureRng::new(); @@ -2111,7 +2214,7 @@ mod tests { } // Measure noise in RGSW ciphertexts of ideal LWE secrets - if false { + if true { let gadget_vec = gadget_vector( bool_evaluator.parameters.rlwe_logq, bool_evaluator.parameters.logb_rgsw, @@ -2147,7 +2250,7 @@ mod tests { } // measure noise grwoth in RLWExRGSW - if false { + if true { let mut rng = DefaultSecureRng::new(); let mut carry_m = vec![0u64; rlwe_n]; RandomUniformDist::random_fill(&mut rng, &rlwe_q, carry_m.as_mut_slice()); @@ -2160,7 +2263,7 @@ mod tests { let mul_mod = |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( &mut rlwe_ct, server_key_eval.rgsw_ct_lwe_si(i), @@ -2257,60 +2360,8 @@ mod tests { let bool_evaluator = BoolEvaluator::>, 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::>, 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 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 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( &lwe0, &lwe1, @@ -2338,6 +2389,45 @@ mod tests { // 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 PBSTracer::with_local(|t| { 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 @@ -2373,6 +2474,9 @@ mod tests { assert!(m_expected == m_back, "Expected {m_expected}, got {m_back}"); m1 = m0; m0 = m_expected; + + lwe1 = lwe0; + lwe0 = lwe_out; } } } diff --git a/src/bool/parameters.rs b/src/bool/parameters.rs index f211d51..ff78787 100644 --- a/src/bool/parameters.rs +++ b/src/bool/parameters.rs @@ -38,17 +38,16 @@ pub(super) const SP_BOOL_PARAMS: BoolParameters = BoolParameters:: { }; pub(super) const MP_BOOL_PARAMS: BoolParameters = BoolParameters:: { - 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, 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, g: 5, w: 1, @@ -60,7 +59,7 @@ mod tests { #[test] fn find_prime() { - let bits = 54; + let bits = 61; 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 d1044ab..a9e71ab 100644 --- a/src/decomposer.rs +++ b/src/decomposer.rs @@ -158,9 +158,9 @@ mod tests { #[test] 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(); @@ -174,7 +174,7 @@ mod tests { }; let decomposer = DefaultDecomposer::new(q, logb, d); let modq_op = ModularOpsU64::new(q); - for _ in 0..100 { + for _ in 0..1000 { let value = rng.gen_range(0..q); let limbs = decomposer.decompose(&value); let value_back = decomposer.recompose(&limbs, &modq_op); diff --git a/src/lwe.rs b/src/lwe.rs index 76899b8..acb6333 100644 --- a/src/lwe.rs +++ b/src/lwe.rs @@ -176,8 +176,6 @@ pub fn lwe_ksk_keygen< b = operator.add(&b, &e); *lwe_b = b; - - // dbg!(&lwe.as_mut(), &f); }) }, ); @@ -323,13 +321,13 @@ mod tests { #[test] fn key_switch_works() { - let logq = 16; + let logq = 18; let logp = 2; let q = 1u64 << logq; let lwe_in_n = 2048; let lwe_out_n = 493; 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_out = LweSecret::random(lwe_out_n >> 1, lwe_out_n); diff --git a/src/random.rs b/src/random.rs index fb7c00a..9ad2c44 100644 --- a/src/random.rs +++ b/src/random.rs @@ -141,7 +141,7 @@ impl RandomGaussianDist<[u64]> for DefaultSecureRng { type Parameters = u64; fn random_fill(&mut self, parameters: &Self::Parameters, container: &mut [u64]) { izip!( - rand_distr::Normal::new(0.0, 3.2f64) + rand_distr::Normal::new(0.0, 3.19f64) .unwrap() .sample_iter(&mut self.rng), container.iter_mut() @@ -162,7 +162,7 @@ impl RandomGaussianDist<[u32]> for DefaultSecureRng { type Parameters = u32; fn random_fill(&mut self, parameters: &Self::Parameters, container: &mut [u32]) { izip!( - rand_distr::Normal::new(0.0, 3.2f32) + rand_distr::Normal::new(0.0, 3.19f32) .unwrap() .sample_iter(&mut self.rng), container.iter_mut() diff --git a/src/rgsw.rs b/src/rgsw.rs index bcb3aa5..be144bc 100644 --- a/src/rgsw.rs +++ b/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) } -/// 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 + VectorOps, - NttOp: Ntt, - 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 - ::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 + VectorOps, - NttOp: Ntt, - 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 - ::R: RowMut, - Mmut::R: TryConvertFrom<[S], Parameters = Mmut::MatElement> + RowEntity, - Mmut::MatElement: Copy + Sub, -{ - 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>( write_to_row: &mut [R::Element], matrix_a: &[R], @@ -958,8 +834,9 @@ pub(crate) fn secret_key_encrypt_rgsw< p_rng: &mut PR, rng: &mut R, ) where - ::R: RowMut + RowEntity + TryConvertFrom<[S], Parameters = Mmut::MatElement>, - Mmut::MatElement: Copy, + ::R: + RowMut + RowEntity + TryConvertFrom<[S], Parameters = Mmut::MatElement> + Debug, + Mmut::MatElement: Copy + Debug, { let d = gadget_vector.len(); 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 + VectorOps, + NttOp: Ntt, + 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 + ::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 + VectorOps, + NttOp: Ntt, + 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 + ::R: RowMut, + Mmut::R: TryConvertFrom<[S], Parameters = Mmut::MatElement> + RowEntity, + Mmut::MatElement: Copy + Sub, +{ + 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. /// /// - 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< - M: MatrixMut, + M: Matrix, + Mmut: MatrixMut, ModOp: VectorOps, NttOp: Ntt, S, @@ -1208,14 +1210,14 @@ pub(crate) fn public_key_encrypt_rlwe< + RandomUniformDist<[u8], Parameters = u8> + RandomUniformDist, >( - rlwe_out: &mut M, + rlwe_out: &mut Mmut, pk: &M, m: &[M::MatElement], mod_op: &ModOp, ntt_op: &NttOp, rng: &mut R, ) where - ::R: RowMut + TryConvertFrom<[S], Parameters = M::MatElement> + RowEntity, + ::R: RowMut + TryConvertFrom<[S], Parameters = M::MatElement> + RowEntity, M::MatElement: Copy, S: Zero + Signed + Copy, { @@ -1226,12 +1228,12 @@ pub(crate) fn public_key_encrypt_rlwe< let mut u = vec![S::zero(); ring_size]; 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()); - 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)); - 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)); // a*u @@ -1419,8 +1421,9 @@ pub(crate) mod tests { }; 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] @@ -1477,6 +1480,9 @@ pub(crate) mod tests { .map(|v| (((*v as f64 * p as f64) / q as f64).round() as u64) % p) .collect_vec(); assert_eq!(m0, m_back); + + let noise = measure_noise(&rlwe_in_ct, &encoded_m, &ntt_op, &mod_op, s.values()); + println!("Noise: {noise}"); } #[test] @@ -1642,11 +1648,11 @@ pub(crate) mod tests { #[test] 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 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); @@ -1685,6 +1691,7 @@ pub(crate) mod tests { } } + // Encrypt m as RGSW ciphertext RGSW(m) using supplied public key fn _pk_encrypt_rgsw( m: &[u64], public_key: &RlwePublicKey>, DefaultSecureRng>, @@ -1821,8 +1828,8 @@ pub(crate) mod tests { 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 d_rgsw = 3; + let logb = 15; 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 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 let public_key = { let mut pk_seed = [0u8; 32]; @@ -1856,11 +1865,23 @@ pub(crate) mod tests { // RGSW(carry_m) let mut rgsw_carrym = _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 = 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]; m[thread_rng().gen_range(0..ring_size) as usize] = q - 1; let rgsw_m = { @@ -1879,22 +1900,54 @@ pub(crate) mod tests { ); // 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 ###########"); _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] fn sk_rgsw_by_rgsw() { - let logq = 31; + let logq = 60; let logp = 2; - let ring_size = 1 << 10; + let ring_size = 1 << 11; let q = generate_prime(logq, ring_size, 1u64 << logq).unwrap(); 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); @@ -1918,11 +1971,13 @@ pub(crate) mod tests { .for_each(|ri| ntt_op.backward(ri.as_mut())); 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 = 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]; 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); @@ -2057,9 +2112,6 @@ pub(crate) mod tests { println!("Ksk noise: {noise}"); } - // FIXME(Jay): Galios autormophism will incur high error unless we fix in - // accurate decomoposition of Decomposer when q is prime assert_eq!(m_k_back, m_k); - // dbg!(m_k_back, m_k, q); } }