From 9f229fe2a98cfbd7e2e85f18a17670078ca46c99 Mon Sep 17 00:00:00 2001 From: Janmajaya Mall Date: Sun, 12 May 2024 15:55:15 +0530 Subject: [PATCH] test noise growth a bit more --- src/lib.rs | 1 + src/noise.rs | 245 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/rgsw.rs | 94 ++++++++++++++++++++ 3 files changed, 340 insertions(+) create mode 100644 src/noise.rs diff --git a/src/lib.rs b/src/lib.rs index 7553168..2c77692 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,7 @@ mod bool; mod decomposer; mod lwe; mod multi_party; +mod noise; mod ntt; mod num; mod random; diff --git a/src/noise.rs b/src/noise.rs new file mode 100644 index 0000000..41b56a2 --- /dev/null +++ b/src/noise.rs @@ -0,0 +1,245 @@ +#[cfg(test)] +mod tests { + use rand::{thread_rng, Rng}; + + use crate::{ + backend::{ArithmeticOps, ModInit, ModularOpsU64}, + decomposer::{gadget_vector, Decomposer, DefaultDecomposer}, + ntt::{Ntt, NttBackendU64, NttInit}, + random::{DefaultSecureRng, RandomGaussianDist, RandomUniformDist}, + rgsw::{ + less1_rlwe_by_rgsw, measure_noise, rgsw_by_rgsw_inplace, rlwe_by_rgsw, + secret_key_encrypt_rgsw, secret_key_encrypt_rlwe, RgswCiphertext, + RgswCiphertextEvaluationDomain, RlweCiphertext, RlweSecret, SeededRgswCiphertext, + SeededRlweCiphertext, + }, + utils::{generate_prime, negacyclic_mul}, + Matrix, Row, Secret, + }; + + // Test B part with limbd -1 when variance of m is 1 + #[test] + fn trial() { + let logq = 28; + let ring_size = 1 << 10; + let q = generate_prime(logq, (ring_size as u64) << 1, 1 << logq).unwrap(); + let logb = 7; + let d0 = 3; + let d1 = d0 - 1; + + let sk = RlweSecret::random((ring_size >> 1) as usize, ring_size as usize); + + let mut rng = DefaultSecureRng::new(); + let gadget_vector = gadget_vector(logq, logb, d0); + + for i in 0..100 { + // m should have norm 1 + let mut m0 = vec![0u64; ring_size as usize]; + m0[thread_rng().gen_range(0..ring_size)] = 1; + + let modq_op = ModularOpsU64::new(q); + let nttq_op = NttBackendU64::new(q, ring_size); + + // Encrypt RGSW(m0) + let mut rgsw_seed = [0u8; 32]; + rng.fill_bytes(&mut rgsw_seed); + let mut seeded_rgsw = + SeededRgswCiphertext::>, _>::empty(ring_size, d0, rgsw_seed, q); + let mut p_rng = DefaultSecureRng::new_seeded(rgsw_seed); + secret_key_encrypt_rgsw( + &mut seeded_rgsw.data, + &m0, + &gadget_vector, + sk.values(), + &modq_op, + &nttq_op, + &mut p_rng, + &mut rng, + ); + + // Encrypt RLWE(m1) + let mut m1 = vec![0u64; ring_size]; + RandomUniformDist::random_fill(&mut rng, &q, m1.as_mut_slice()); + let mut rlwe_seed = [0u8; 32]; + rng.fill_bytes(&mut rlwe_seed); + let mut seeded_rlwe: SeededRlweCiphertext, [u8; 32]> = + SeededRlweCiphertext::, _>::empty(ring_size, rlwe_seed, q); + let mut p_rng = DefaultSecureRng::new_seeded(rlwe_seed); + secret_key_encrypt_rlwe( + &m1, + &mut seeded_rlwe.data, + sk.values(), + &modq_op, + &nttq_op, + &mut p_rng, + &mut rng, + ); + + let mut rlwe = RlweCiphertext::>, DefaultSecureRng>::from(&seeded_rlwe); + let rgsw = RgswCiphertextEvaluationDomain::<_, DefaultSecureRng, NttBackendU64>::from( + &seeded_rgsw, + ); + + // RLWE(m0m1) = RLWE(m1) x RGSW(m0) + let mut scratch = vec![vec![0u64; ring_size]; d0 + 2]; + let decomposer = DefaultDecomposer::new(q, logb, d0); + less1_rlwe_by_rgsw( + &mut rlwe, + &rgsw.data, + &mut scratch, + &decomposer, + &nttq_op, + &modq_op, + 0, + 1, + ); + // rlwe_by_rgsw( + // &mut rlwe, + // &rgsw.data, + // &mut scratch, + // &decomposer, + // &nttq_op, + // &modq_op, + // ); + + // measure noise + let mul_mod = |v0: &u64, v1: &u64| ((*v0 as u128 * *v1 as u128) % q as u128) as u64; + let m0m1 = negacyclic_mul(&m0, &m1, mul_mod, q); + let noise = measure_noise(&rlwe, &m0m1, &nttq_op, &modq_op, sk.values()); + println!("Noise: {noise}"); + } + } + + // Test B part with limbd -1 when variance of m is 1 + #[test] + fn rgsw_saver() { + let logq = 60; + let ring_size = 1 << 11; + let q = generate_prime(logq, (ring_size as u64) << 1, 1 << logq).unwrap(); + let logb = 12; + let d0 = 4; + + let sk = RlweSecret::random((ring_size >> 1) as usize, ring_size as usize); + + let mut rng = DefaultSecureRng::new(); + let gadget_vector = gadget_vector(logq, logb, d0); + + let decomposer = DefaultDecomposer::new(q, logb, d0); + + for i in 0..100 { + let modq_op = ModularOpsU64::new(q); + let nttq_op = NttBackendU64::new(q, ring_size); + + // Encrypt RGSW(m0) + let mut m0 = vec![0u64; ring_size as usize]; + m0[thread_rng().gen_range(0..ring_size)] = 1; + let mut rgsw_seed = [0u8; 32]; + rng.fill_bytes(&mut rgsw_seed); + let mut seeded_rgsw0 = + SeededRgswCiphertext::>, _>::empty(ring_size, d0, rgsw_seed, q); + let mut p_rng = DefaultSecureRng::new_seeded(rgsw_seed); + secret_key_encrypt_rgsw( + &mut seeded_rgsw0.data, + &m0, + &gadget_vector, + sk.values(), + &modq_op, + &nttq_op, + &mut p_rng, + &mut rng, + ); + + // Encrypt RGSW(m1) + let mut m1 = vec![0u64; ring_size as usize]; + m1[thread_rng().gen_range(0..ring_size)] = 1; + let mut rgsw_seed = [0u8; 32]; + rng.fill_bytes(&mut rgsw_seed); + let mut seeded_rgsw1 = + SeededRgswCiphertext::>, _>::empty(ring_size, d0, rgsw_seed, q); + let mut p_rng = DefaultSecureRng::new_seeded(rgsw_seed); + secret_key_encrypt_rgsw( + &mut seeded_rgsw1.data, + &m1, + &gadget_vector, + sk.values(), + &modq_op, + &nttq_op, + &mut p_rng, + &mut rng, + ); + + // TODO(Jay): Why cant you create RgswCIphertext from SeededRgswCiphertext? + let mut rgsw0 = { + let mut evl_tmp = + RgswCiphertextEvaluationDomain::<_, DefaultSecureRng, NttBackendU64>::from( + &seeded_rgsw0, + ); + evl_tmp + .data + .iter_mut() + .for_each(|ri| nttq_op.backward(ri.as_mut())); + evl_tmp.data + }; + let rgsw1 = RgswCiphertextEvaluationDomain::<_, DefaultSecureRng, NttBackendU64>::from( + &seeded_rgsw1, + ); + let mut scratch_matrix_d_plus_rgsw_by_ring = vec![vec![0u64; ring_size]; d0 + (d0 * 4)]; + + // RGSW(m0m1) = RGSW(m0)xRGSW(m1) + rgsw_by_rgsw_inplace( + &mut rgsw0, + &rgsw1.data, + &decomposer, + &mut scratch_matrix_d_plus_rgsw_by_ring, + &nttq_op, + &modq_op, + ); + + // send RGSW(m0m1) to Evaluation domain + let mut rgsw01 = rgsw0; + rgsw01 + .iter_mut() + .for_each(|v| nttq_op.forward(v.as_mut_slice())); + + // RLWE(m2) + let mut m2 = vec![0u64; ring_size as usize]; + RandomUniformDist::random_fill(&mut rng, &q, m2.as_mut_slice()); + let mut rlwe_seed = [0u8; 32]; + rng.fill_bytes(&mut rlwe_seed); + let mut seeded_rlwe = + SeededRlweCiphertext::, _>::empty(ring_size, rlwe_seed, q); + let mut p_rng = DefaultSecureRng::new_seeded(rlwe_seed); + secret_key_encrypt_rlwe( + &m2, + &mut seeded_rlwe.data, + sk.values(), + &modq_op, + &nttq_op, + &mut p_rng, + &mut rng, + ); + + let mut rlwe = RlweCiphertext::>, DefaultSecureRng>::from(&seeded_rlwe); + + // RLWE(m0m1m2) = RLWE(m2) x RGSW(m0m1) + let mut scratch_matrix_dplus2_ring = vec![vec![0u64; ring_size]; d0 + 2]; + less1_rlwe_by_rgsw( + &mut rlwe, + &rgsw01, + &mut scratch_matrix_dplus2_ring, + &decomposer, + &nttq_op, + &modq_op, + 1, + 2, + ); + + let mul_mod = |v0: &u64, v1: &u64| ((*v0 as u128 * *v1 as u128) % q as u128) as u64; + let m0m1 = negacyclic_mul(&m0, &m1, mul_mod, q); + let m0m1m2 = negacyclic_mul(&m2, &m0m1, mul_mod, q); + let noise = measure_noise(&rlwe.data, &m0m1m2, &nttq_op, &modq_op, sk.values()); + + println!("Noise: {noise}"); + } + } +} diff --git a/src/rgsw.rs b/src/rgsw.rs index be144bc..0b4cd2f 100644 --- a/src/rgsw.rs +++ b/src/rgsw.rs @@ -598,6 +598,100 @@ pub(crate) fn galois_auto< .copy_from_slice(tmp_rlwe_out[1].as_ref()); } +/// Returns RLWE(m0m1) = RLWE(m0) x RGSW(m1). Mutates rlwe_in inplace to equal +/// RLWE(m0m1) +/// +/// - rlwe_in: is RLWE(m0) with polynomials in coefficient domain +/// - rgsw_in: is RGSW(m1) with polynomials in evaluation domain +/// - scratch_matrix_d_ring: is a matrix of dimension (d_rgsw, ring_size) used +/// as scratch space to store decomposed Ring elements temporarily +pub(crate) fn less1_rlwe_by_rgsw< + Mmut: MatrixMut, + MT: Matrix + MatrixMut + IsTrivial, + D: Decomposer, + ModOp: VectorOps, + NttOp: Ntt, +>( + rlwe_in: &mut MT, + rgsw_in: &Mmut, + scratch_matrix_dplus2_ring: &mut Mmut, + decomposer: &D, + ntt_op: &NttOp, + mod_op: &ModOp, + skip0: usize, + skip1: usize, +) where + Mmut::MatElement: Copy + Zero, + ::R: RowMut, + ::R: RowMut, +{ + let d_rgsw = decomposer.d(); + assert!(scratch_matrix_dplus2_ring.dimension() == (d_rgsw + 2, rlwe_in.dimension().1)); + assert!(rgsw_in.dimension() == (d_rgsw * 4, rlwe_in.dimension().1)); + + // decomposed RLWE x RGSW + let (rlwe_dash_nsm, rlwe_dash_m) = rgsw_in.split_at_row(d_rgsw * 2); + let (scratch_matrix_d_ring, scratch_rlwe_out) = + scratch_matrix_dplus2_ring.split_at_row_mut(d_rgsw); + scratch_rlwe_out[0].as_mut().fill(Mmut::MatElement::zero()); + scratch_rlwe_out[1].as_mut().fill(Mmut::MatElement::zero()); + // RLWE_in = a_in, b_in; RLWE_out = a_out, b_out + if !rlwe_in.is_trivial() { + // a_in = 0 when RLWE_in is trivial RLWE ciphertext + // decomp + decompose_r(rlwe_in.get_row_slice(0), scratch_matrix_d_ring, decomposer); + scratch_matrix_d_ring + .iter_mut() + .for_each(|r| ntt_op.forward(r.as_mut())); + // a_out += decomp \cdot RLWE_A'(-sm) + routine( + scratch_rlwe_out[0].as_mut(), + scratch_matrix_d_ring[skip0..].as_ref(), + &rlwe_dash_nsm[skip0..d_rgsw], + mod_op, + ); + // b_out += decomp \cdot RLWE_B'(-sm) + routine( + scratch_rlwe_out[1].as_mut(), + scratch_matrix_d_ring[skip0..].as_ref(), + &rlwe_dash_nsm[d_rgsw + skip0..], + mod_op, + ); + } + // decomp + decompose_r(rlwe_in.get_row_slice(1), scratch_matrix_d_ring, decomposer); + scratch_matrix_d_ring + .iter_mut() + .for_each(|r| ntt_op.forward(r.as_mut())); + // a_out += decomp \cdot RLWE_A'(m) + routine( + scratch_rlwe_out[0].as_mut(), + scratch_matrix_d_ring[skip1..].as_ref(), + &rlwe_dash_m[skip1..d_rgsw], + mod_op, + ); + // b_out += decomp \cdot RLWE_B'(m) + routine( + scratch_rlwe_out[1].as_mut(), + scratch_matrix_d_ring[skip1..].as_ref(), + &rlwe_dash_m[d_rgsw + skip1..], + mod_op, + ); + + // transform rlwe_out to coefficient domain + scratch_rlwe_out + .iter_mut() + .for_each(|r| ntt_op.backward(r.as_mut())); + + rlwe_in + .get_row_mut(0) + .copy_from_slice(scratch_rlwe_out[0].as_mut()); + rlwe_in + .get_row_mut(1) + .copy_from_slice(scratch_rlwe_out[1].as_mut()); + rlwe_in.set_not_trivial(); +} + /// Returns RLWE(m0m1) = RLWE(m0) x RGSW(m1). Mutates rlwe_in inplace to equal /// RLWE(m0m1) ///