#[cfg(test)] mod tests { use rand::{thread_rng, Rng}; use crate::{ backend::{ArithmeticOps, ModInit, ModularOpsU64}, decomposer::{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 decomposer = DefaultDecomposer::new(q, logb, d0); let gadget_vector = decomposer.gadget_vector(); 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]; 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 decomposer = DefaultDecomposer::new(q, logb, d0); let gadget_vector = decomposer.gadget_vector(); 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}"); } } }