mirror of
https://github.com/arnaucube/phantom-zone.git
synced 2026-01-07 22:51:29 +01:00
multi-party NAND gate works
This commit is contained in:
@@ -192,7 +192,11 @@ fn aggregate_multi_party_server_key_shares<
|
||||
ModOp: VectorOps<Element = M::MatElement> + ModInit<Element = M::MatElement>,
|
||||
NttOp: Ntt<Element = M::MatElement> + NttInit<Element = M::MatElement>,
|
||||
>(
|
||||
shares: &[CommonReferenceSeededMultiPartyServerKeyShare<M, BoolParameters<M::MatElement>, S>],
|
||||
mut shares: &[CommonReferenceSeededMultiPartyServerKeyShare<
|
||||
M,
|
||||
BoolParameters<M::MatElement>,
|
||||
S,
|
||||
>],
|
||||
d_rgsw_decomposer: &D,
|
||||
) -> SeededMultiPartyServerKey<M, S, BoolParameters<M::MatElement>>
|
||||
where
|
||||
@@ -241,20 +245,26 @@ where
|
||||
// rgsw ciphertext (most expensive part!)
|
||||
let lwe_n = parameters.lwe_n;
|
||||
let mut scratch_d_plus_rgsw_by_ring = M::zeros(d_rgsw + (d_rgsw * 4), rlwe_n);
|
||||
let mut tmp_rgsw = M::zeros(d_rgsw * 2 * 2, rlwe_n);
|
||||
let rgsw_cts = (0..lwe_n)
|
||||
.into_iter()
|
||||
.map(|index| {
|
||||
// copy over rgsw ciphertext for index^th secret element from first share and
|
||||
// send it to evaluation domain
|
||||
// treat it as accumulating rgsw ciphertext
|
||||
let mut rgsw_i = shares[0].rgsw_cts[index].clone();
|
||||
rgsw_i
|
||||
.iter_rows_mut()
|
||||
.for_each(|ri| rlweq_nttop.forward(ri.as_mut()));
|
||||
|
||||
shares.iter().skip(1).for_each(|si| {
|
||||
// copy over si's RGSW[index] ciphertext and send to evaluation domain
|
||||
izip!(tmp_rgsw.iter_rows_mut(), si.rgsw_cts[index].iter_rows()).for_each(
|
||||
|(to_ri, from_ri)| {
|
||||
to_ri.as_mut().copy_from_slice(from_ri.as_ref());
|
||||
rlweq_nttop.forward(to_ri.as_mut())
|
||||
},
|
||||
);
|
||||
|
||||
rgsw_by_rgsw_inplace(
|
||||
&mut rgsw_i,
|
||||
&si.rgsw_cts[index],
|
||||
&tmp_rgsw,
|
||||
d_rgsw_decomposer,
|
||||
&mut scratch_d_plus_rgsw_by_ring,
|
||||
&rlweq_nttop,
|
||||
@@ -262,10 +272,6 @@ where
|
||||
);
|
||||
});
|
||||
|
||||
// send final rgsw ciphertext of secret element at index to coefficient domain
|
||||
rgsw_i
|
||||
.iter_rows_mut()
|
||||
.for_each(|ri| rlweq_nttop.backward(ri.as_mut()));
|
||||
rgsw_i
|
||||
})
|
||||
.collect_vec();
|
||||
@@ -1629,14 +1635,17 @@ impl WithLocal for PBSTracer<Vec<Vec<u64>>> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rand::{thread_rng, Rng};
|
||||
|
||||
use crate::{
|
||||
backend::ModularOpsU64,
|
||||
bool,
|
||||
ntt::NttBackendU64,
|
||||
random::DEFAULT_RNG,
|
||||
rgsw::{
|
||||
self, measure_noise, secret_key_encrypt_rlwe, tests::_measure_noise_rgsw,
|
||||
RgswCiphertextEvaluationDomain, SeededRgswCiphertext, SeededRlweCiphertext,
|
||||
self, measure_noise, public_key_encrypt_rlwe, secret_key_encrypt_rlwe,
|
||||
tests::_measure_noise_rgsw, RgswCiphertext, RgswCiphertextEvaluationDomain,
|
||||
SeededRgswCiphertext, SeededRlweCiphertext,
|
||||
},
|
||||
utils::negacyclic_mul,
|
||||
};
|
||||
@@ -1807,7 +1816,7 @@ mod tests {
|
||||
let bool_evaluator =
|
||||
BoolEvaluator::<Vec<Vec<u64>>, u64, NttBackendU64, ModularOpsU64>::new(MP_BOOL_PARAMS);
|
||||
|
||||
let no_of_parties = 5;
|
||||
let no_of_parties = 10;
|
||||
let parties = (0..no_of_parties)
|
||||
.map(|_| bool_evaluator.client_key())
|
||||
.collect_vec();
|
||||
@@ -1862,14 +1871,20 @@ mod tests {
|
||||
}
|
||||
};
|
||||
|
||||
let lwe_q = bool_evaluator.parameters.lwe_q;
|
||||
let rlwe_q = bool_evaluator.parameters.rlwe_q;
|
||||
let d_rgsw = bool_evaluator.parameters.d_rgsw;
|
||||
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 lwe_modop = &bool_evaluator.lwe_modop;
|
||||
let rlwe_nttop = &bool_evaluator.rlwe_nttop;
|
||||
let rlwe_modop = &bool_evaluator.rlwe_modop;
|
||||
let rlwe_decomposer = &bool_evaluator.decomposer_rlwe;
|
||||
|
||||
// test LWE ksk from RLWE -> LWE
|
||||
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;
|
||||
if true {
|
||||
let logp = 2;
|
||||
let lwe_modop = &bool_evaluator.lwe_modop;
|
||||
let mut rng = DefaultSecureRng::new();
|
||||
|
||||
let m = 1;
|
||||
@@ -1911,15 +1926,13 @@ mod tests {
|
||||
println!("Noise: {noise}");
|
||||
}
|
||||
|
||||
{
|
||||
let rlwe_n = bool_evaluator.parameters.rlwe_n;
|
||||
let rlwe_q = bool_evaluator.parameters.rlwe_q;
|
||||
// Measure noise in RGSW ciphertexts of ideal LWE secrets
|
||||
if false {
|
||||
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])
|
||||
@@ -1937,7 +1950,7 @@ mod tests {
|
||||
.for_each(|ri| rlwe_nttop.backward(ri.as_mut()));
|
||||
|
||||
println!("####### Noise in RGSW(X^s_{i}) #######");
|
||||
let noise = _measure_noise_rgsw(
|
||||
_measure_noise_rgsw(
|
||||
&rgsw_si,
|
||||
&si_poly,
|
||||
ideal_client_key.sk_rlwe.values(),
|
||||
@@ -1948,106 +1961,107 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
// measure noise grwoth in RLWExRGSW
|
||||
if false {
|
||||
let rlwe_q = bool_evaluator.parameters.rlwe_q;
|
||||
let rlwe_n = bool_evaluator.parameters.rlwe_n;
|
||||
let logp = 3;
|
||||
let p = 1 << logp;
|
||||
let rlwe_modop = &bool_evaluator.rlwe_modop;
|
||||
let rlwe_nttop = &bool_evaluator.rlwe_nttop;
|
||||
let d_rgsw = bool_evaluator.parameters.d_rgsw;
|
||||
|
||||
let mut rng = DefaultSecureRng::new();
|
||||
let mut m = vec![0u64; rlwe_n];
|
||||
RandomUniformDist::random_fill(&mut rng, &p, m.as_mut_slice());
|
||||
let mut carry_m = vec![0u64; rlwe_n];
|
||||
RandomUniformDist::random_fill(&mut rng, &rlwe_q, carry_m.as_mut_slice());
|
||||
|
||||
// Encode message m
|
||||
let encoded_m = m
|
||||
.iter()
|
||||
.map(|el| ((*el as f64 * rlwe_q as f64) / (p as f64)).round() as u64)
|
||||
.collect_vec();
|
||||
// RGSW(carrym)
|
||||
let trivial_rlwect = vec![vec![0u64; rlwe_n], carry_m.clone()];
|
||||
let mut rlwe_ct = RlweCiphertext::<_, DefaultSecureRng>::from_raw(trivial_rlwect, true);
|
||||
|
||||
// Encrypt encoded m -> RLWE(m)
|
||||
let mut rlwe_seed = [0u8; 32];
|
||||
rng.fill_bytes(&mut rlwe_seed);
|
||||
let mut seeded_rlwe_ct = SeededRlweCiphertext::empty(rlwe_n, rlwe_seed, rlwe_q);
|
||||
let mut rlwe_prng = DefaultSecureRng::new_seeded(rlwe_seed);
|
||||
secret_key_encrypt_rlwe(
|
||||
&encoded_m,
|
||||
&mut seeded_rlwe_ct.data,
|
||||
ideal_client_key.sk_rlwe.values(),
|
||||
rlwe_modop,
|
||||
rlwe_nttop,
|
||||
&mut rlwe_prng,
|
||||
&mut rng,
|
||||
);
|
||||
// public_key_encrypt_rgsw(out_rgsw, m, public_key, gadget_vector, mod_op,
|
||||
// ntt_op, rng);
|
||||
let mut rlwe_ct =
|
||||
RlweCiphertext::<Vec<Vec<u64>>, DefaultSecureRng>::from(&seeded_rlwe_ct);
|
||||
let mut scratch_matrix_dplus2_ring = vec![vec![0u64; rlwe_n]; d_rgsw + 2];
|
||||
let mul_mod =
|
||||
|v0: &u64, v1: &u64| (((*v0 as u128 * *v1 as u128) % (rlwe_q as u128)) as u64);
|
||||
|
||||
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;
|
||||
for i in 0..450 {
|
||||
rlwe_by_rgsw(
|
||||
&mut rlwe_ct,
|
||||
server_key_eval.rgsw_ct_lwe_si(index),
|
||||
// &rgsw_ct.data,
|
||||
server_key_eval.rgsw_ct_lwe_si(i),
|
||||
&mut scratch_matrix_dplus2_ring,
|
||||
rlwe_decomposer,
|
||||
rlwe_nttop,
|
||||
rlwe_modop,
|
||||
);
|
||||
|
||||
// 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(
|
||||
// carry_m[X] * s_i[X]
|
||||
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;
|
||||
}
|
||||
carry_m = negacyclic_mul(&carry_m, &si_poly, mul_mod, rlwe_q);
|
||||
|
||||
let noise = measure_noise(
|
||||
&rlwe_ct,
|
||||
ideal_client_key.sk_rlwe.values(),
|
||||
&mut encoded_m_back,
|
||||
&carry_m,
|
||||
rlwe_nttop,
|
||||
rlwe_modop,
|
||||
ideal_client_key.sk_rlwe.values(),
|
||||
);
|
||||
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();
|
||||
println!("Noise RLWE(carry_m) accumulating {i}^th secret monomial: {noise}");
|
||||
}
|
||||
}
|
||||
|
||||
// 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 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);
|
||||
// Check galois keys
|
||||
if false {
|
||||
let g = bool_evaluator.g() as isize;
|
||||
let mut rng = DefaultSecureRng::new();
|
||||
let mut scratch_matrix_dplus2_ring = vec![vec![0u64; rlwe_n]; d_rgsw + 2];
|
||||
for i in [g, -g] {
|
||||
let mut m = vec![0u64; rlwe_n];
|
||||
RandomUniformDist::random_fill(&mut rng, &rlwe_q, m.as_mut_slice());
|
||||
let mut rlwe_ct = {
|
||||
let mut data = vec![vec![0u64; rlwe_n]; 2];
|
||||
public_key_encrypt_rlwe(
|
||||
&mut data,
|
||||
&collective_pk.key,
|
||||
&m,
|
||||
rlwe_modop,
|
||||
rlwe_nttop,
|
||||
&mut rng,
|
||||
);
|
||||
RlweCiphertext::<_, DefaultSecureRng>::from_raw(data, false)
|
||||
};
|
||||
|
||||
let auto_key = server_key_eval.galois_key_for_auto(i);
|
||||
let (auto_map_index, auto_map_sign) = generate_auto_map(rlwe_n, i);
|
||||
galois_auto(
|
||||
&mut rlwe_ct,
|
||||
auto_key,
|
||||
&mut scratch_matrix_dplus2_ring,
|
||||
&auto_map_index,
|
||||
&auto_map_sign,
|
||||
rlwe_modop,
|
||||
rlwe_nttop,
|
||||
rlwe_decomposer,
|
||||
);
|
||||
|
||||
// send m(X) -> m(X^i)
|
||||
let mut m_k = vec![0u64; rlwe_n];
|
||||
izip!(m.iter(), auto_map_index.iter(), auto_map_sign.iter()).for_each(
|
||||
|(mi, to_index, to_sign)| {
|
||||
if !to_sign {
|
||||
m_k[*to_index] = rlwe_q - *mi;
|
||||
} else {
|
||||
m_k[*to_index] = *mi;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// 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,
|
||||
&m_k,
|
||||
rlwe_nttop,
|
||||
rlwe_modop,
|
||||
ideal_client_key.sk_rlwe.values(),
|
||||
);
|
||||
|
||||
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);
|
||||
|
||||
// println!("M_back:{:?} \n Expected_m:{:?}", m_back,
|
||||
// expected_m);
|
||||
println!("Noise after auto k={i}: {noise}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2122,7 +2136,7 @@ mod tests {
|
||||
let mut m0 = true;
|
||||
let mut m1 = false;
|
||||
|
||||
for _ in 0..100 {
|
||||
for _ in 0..500 {
|
||||
let lwe0 = bool_evaluator.pk_encrypt(&collective_pk.key, m0);
|
||||
let lwe1 = bool_evaluator.pk_encrypt(&collective_pk.key, m1);
|
||||
|
||||
@@ -2168,11 +2182,11 @@ mod tests {
|
||||
.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_back = bool_evaluator.sk_decrypt(&lwe_out, &ideal_client_key);
|
||||
|
||||
dbg!(m_expected, m_back);
|
||||
assert_eq!(m_expected, m_back);
|
||||
m1 = m0;
|
||||
m0 = m_back;
|
||||
m0 = m_expected;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,10 +28,10 @@ pub(super) const SP_BOOL_PARAMS: BoolParameters<u64> = BoolParameters::<u64> {
|
||||
lwe_logq: 16,
|
||||
br_q: 1 << 10,
|
||||
rlwe_n: 1 << 10,
|
||||
lwe_n: 200,
|
||||
d_rgsw: 3,
|
||||
logb_rgsw: 8,
|
||||
d_lwe: 3,
|
||||
lwe_n: 493,
|
||||
d_rgsw: 4,
|
||||
logb_rgsw: 7,
|
||||
d_lwe: 4,
|
||||
logb_lwe: 4,
|
||||
g: 5,
|
||||
w: 1,
|
||||
@@ -40,15 +40,15 @@ pub(super) const SP_BOOL_PARAMS: BoolParameters<u64> = BoolParameters::<u64> {
|
||||
pub(super) const MP_BOOL_PARAMS: BoolParameters<u64> = BoolParameters::<u64> {
|
||||
rlwe_q: 1152921504606830593,
|
||||
rlwe_logq: 60,
|
||||
lwe_q: 1 << 25,
|
||||
lwe_logq: 25,
|
||||
br_q: 1 << 11,
|
||||
lwe_q: 1 << 20,
|
||||
lwe_logq: 20,
|
||||
br_q: 1 << 12,
|
||||
rlwe_n: 1 << 11,
|
||||
lwe_n: 500,
|
||||
d_rgsw: 10,
|
||||
logb_rgsw: 6,
|
||||
d_lwe: 5,
|
||||
logb_lwe: 5,
|
||||
logb_lwe: 4,
|
||||
g: 5,
|
||||
w: 1,
|
||||
};
|
||||
|
||||
182
src/rgsw.rs
182
src/rgsw.rs
@@ -6,7 +6,7 @@ use std::{
|
||||
};
|
||||
|
||||
use itertools::{izip, Itertools};
|
||||
use num_traits::{PrimInt, ToPrimitive, Zero};
|
||||
use num_traits::{PrimInt, Signed, ToPrimitive, Zero};
|
||||
|
||||
use crate::{
|
||||
backend::{ArithmeticOps, VectorOps},
|
||||
@@ -103,7 +103,7 @@ where
|
||||
}
|
||||
|
||||
impl<M: Matrix + MatrixEntity, S> SeededRgswCiphertext<M, S> {
|
||||
fn from_raw(data: M, seed: S, modulus: M::MatElement) -> SeededRgswCiphertext<M, S> {
|
||||
pub(crate) fn from_raw(data: M, seed: S, modulus: M::MatElement) -> SeededRgswCiphertext<M, S> {
|
||||
assert!(data.dimension().0 % 3 == 0);
|
||||
|
||||
SeededRgswCiphertext {
|
||||
@@ -814,11 +814,22 @@ pub(crate) fn rlwe_by_rgsw<
|
||||
rlwe_in.set_not_trivial();
|
||||
}
|
||||
|
||||
/// Inplace mutates rlwe_0_eval_domain to equal RGSW(m0m1) = RGSW(m0)xRGSW(m1)
|
||||
/// Inplace mutates rlwe_0 to equal RGSW(m0m1) = RGSW(m0)xRGSW(m1)
|
||||
/// in evaluation domain
|
||||
///
|
||||
/// - rgsw_0_eval_domain: RGSW(m0) in evaluation domain
|
||||
/// - rgsw_1: RGSW(m1)
|
||||
/// Warning -
|
||||
/// Pass a fresh RGSW ciphertext as the second operand, i.e. as `rgsw_1`.
|
||||
/// This is to assure minimal error growth in the resulting RGSW ciphertext.
|
||||
/// RGSWxRGSW boils down to d_rgsw*2 RLWExRGSW multiplications. Hence, the noise
|
||||
/// growth in resulting ciphertext depends on the norm of second RGSW
|
||||
/// ciphertext, not the first. This is useful in cases where one is accumulating
|
||||
/// multiple RGSW ciphertexts into 1. In which case, pass the accumulating RGSW
|
||||
/// ciphertext as rlwe_0 (the one with higher noise) and subsequent RGSW
|
||||
/// ciphertexts, with lower noise, to be accumulated as second
|
||||
/// operand.
|
||||
///
|
||||
/// - rgsw_0: RGSW(m0)
|
||||
/// - rgsw_1_eval: RGSW(m1) in Evaluation domain
|
||||
/// - scratch_matrix_d_plus_rgsw_by_ring: scratch space matrix of size
|
||||
/// (d+(d*4))xring_size, where d equals d_rgsw
|
||||
pub(crate) fn rgsw_by_rgsw_inplace<
|
||||
@@ -827,8 +838,8 @@ pub(crate) fn rgsw_by_rgsw_inplace<
|
||||
ModOp: VectorOps<Element = Mmut::MatElement>,
|
||||
NttOp: Ntt<Element = Mmut::MatElement>,
|
||||
>(
|
||||
rgsw_0_eval_domain: &mut Mmut,
|
||||
rgsw_1: &Mmut,
|
||||
rgsw_0: &mut Mmut,
|
||||
rgsw_1_eval: &Mmut,
|
||||
decomposer: &D,
|
||||
scratch_matrix_d_plus_rgsw_by_ring: &mut Mmut,
|
||||
ntt_op: &NttOp,
|
||||
@@ -838,9 +849,9 @@ pub(crate) fn rgsw_by_rgsw_inplace<
|
||||
Mmut::MatElement: Copy + Zero,
|
||||
{
|
||||
let d_rgsw = decomposer.d();
|
||||
assert!(rgsw_0_eval_domain.dimension().0 == 4 * d_rgsw);
|
||||
let ring_size = rgsw_0_eval_domain.dimension().1;
|
||||
assert!(rgsw_1.dimension() == (4 * d_rgsw, ring_size));
|
||||
assert!(rgsw_0.dimension().0 == 4 * d_rgsw);
|
||||
let ring_size = rgsw_0.dimension().1;
|
||||
assert!(rgsw_1_eval.dimension() == (4 * d_rgsw, ring_size));
|
||||
assert!(scratch_matrix_d_plus_rgsw_by_ring.dimension() == (d_rgsw + (d_rgsw * 4), ring_size));
|
||||
|
||||
let (decomp_r_space, rgsw_space) = scratch_matrix_d_plus_rgsw_by_ring.split_at_row_mut(d_rgsw);
|
||||
@@ -854,19 +865,19 @@ pub(crate) fn rgsw_by_rgsw_inplace<
|
||||
rlwe_dash_space_nsm.split_at_mut(d_rgsw);
|
||||
let (rlwe_dash_space_m_parta, rlwe_dash_space_m_partb) = rlwe_dash_space_m.split_at_mut(d_rgsw);
|
||||
|
||||
let (rgsw0_nsm, rgsw0_m) = rgsw_0_eval_domain.split_at_row(d_rgsw * 2);
|
||||
let (rgsw1_nsm, rgsw1_m) = rgsw_1.split_at_row(d_rgsw * 2);
|
||||
let (rgsw0_nsm, rgsw0_m) = rgsw_0.split_at_row(d_rgsw * 2);
|
||||
let (rgsw1_nsm, rgsw1_m) = rgsw_1_eval.split_at_row(d_rgsw * 2);
|
||||
|
||||
// RGSW x RGSW
|
||||
izip!(
|
||||
rgsw1_nsm
|
||||
rgsw0_nsm
|
||||
.iter()
|
||||
.take(d_rgsw)
|
||||
.chain(rgsw1_m.iter().take(d_rgsw)),
|
||||
rgsw1_nsm
|
||||
.chain(rgsw0_m.iter().take(d_rgsw)),
|
||||
rgsw0_nsm
|
||||
.iter()
|
||||
.skip(d_rgsw)
|
||||
.chain(rgsw1_m.iter().skip(d_rgsw)),
|
||||
.chain(rgsw0_m.iter().skip(d_rgsw)),
|
||||
rlwe_dash_space_nsm_parta
|
||||
.iter_mut()
|
||||
.chain(rlwe_dash_space_m_parta.iter_mut()),
|
||||
@@ -883,13 +894,13 @@ pub(crate) fn rgsw_by_rgsw_inplace<
|
||||
routine(
|
||||
rlwe_out_a.as_mut(),
|
||||
decomp_r_space,
|
||||
&rgsw0_nsm[..d_rgsw],
|
||||
&rgsw1_nsm[..d_rgsw],
|
||||
mod_op,
|
||||
);
|
||||
routine(
|
||||
rlwe_out_b.as_mut(),
|
||||
decomp_r_space,
|
||||
&rgsw0_nsm[d_rgsw..],
|
||||
&rgsw1_nsm[d_rgsw..],
|
||||
mod_op,
|
||||
);
|
||||
|
||||
@@ -901,20 +912,25 @@ pub(crate) fn rgsw_by_rgsw_inplace<
|
||||
routine(
|
||||
rlwe_out_a.as_mut(),
|
||||
decomp_r_space,
|
||||
&rgsw0_m[..d_rgsw],
|
||||
&rgsw1_m[..d_rgsw],
|
||||
mod_op,
|
||||
);
|
||||
routine(
|
||||
rlwe_out_b.as_mut(),
|
||||
decomp_r_space,
|
||||
&rgsw0_m[d_rgsw..],
|
||||
&rgsw1_m[d_rgsw..],
|
||||
mod_op,
|
||||
);
|
||||
});
|
||||
|
||||
// copy over RGSW(m0m1) into RGSW(m0)
|
||||
izip!(rgsw_0_eval_domain.iter_rows_mut(), rgsw_space.iter())
|
||||
.for_each(|(to_ri, from_ri)| to_ri.as_mut().copy_from_slice(from_ri.as_ref()))
|
||||
izip!(rgsw_0.iter_rows_mut(), rgsw_space.iter())
|
||||
.for_each(|(to_ri, from_ri)| to_ri.as_mut().copy_from_slice(from_ri.as_ref()));
|
||||
|
||||
// send back to coefficient domain
|
||||
rgsw_0
|
||||
.iter_rows_mut()
|
||||
.for_each(|ri| ntt_op.backward(ri.as_mut()));
|
||||
}
|
||||
|
||||
/// Encrypts message m as a RGSW ciphertext.
|
||||
@@ -1182,6 +1198,66 @@ pub(crate) fn secret_key_encrypt_rlwe<
|
||||
mod_op.elwise_add_mut(b_rlwe_out.as_mut(), sa.as_ref());
|
||||
}
|
||||
|
||||
pub(crate) fn public_key_encrypt_rlwe<
|
||||
M: MatrixMut,
|
||||
ModOp: VectorOps<Element = M::MatElement>,
|
||||
NttOp: Ntt<Element = M::MatElement>,
|
||||
S,
|
||||
R: RandomGaussianDist<[M::MatElement], Parameters = M::MatElement>
|
||||
+ RandomUniformDist<[M::MatElement], Parameters = M::MatElement>
|
||||
+ RandomUniformDist<[u8], Parameters = u8>
|
||||
+ RandomUniformDist<usize, Parameters = usize>,
|
||||
>(
|
||||
rlwe_out: &mut M,
|
||||
pk: &M,
|
||||
m: &[M::MatElement],
|
||||
mod_op: &ModOp,
|
||||
ntt_op: &NttOp,
|
||||
rng: &mut R,
|
||||
) where
|
||||
<M as Matrix>::R: RowMut + TryConvertFrom<[S], Parameters = M::MatElement> + RowEntity,
|
||||
M::MatElement: Copy,
|
||||
S: Zero + Signed + Copy,
|
||||
{
|
||||
let ring_size = m.len();
|
||||
assert!(rlwe_out.dimension() == (2, ring_size));
|
||||
|
||||
let q = mod_op.modulus();
|
||||
|
||||
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);
|
||||
ntt_op.forward(u.as_mut());
|
||||
|
||||
let mut ua = M::R::zeros(ring_size);
|
||||
ua.as_mut().copy_from_slice(pk.get_row_slice(0));
|
||||
let mut ub = M::R::zeros(ring_size);
|
||||
ub.as_mut().copy_from_slice(pk.get_row_slice(1));
|
||||
|
||||
// a*u
|
||||
ntt_op.forward(ua.as_mut());
|
||||
mod_op.elwise_mul_mut(ua.as_mut(), u.as_ref());
|
||||
ntt_op.backward(ua.as_mut());
|
||||
|
||||
// b*u
|
||||
ntt_op.forward(ub.as_mut());
|
||||
mod_op.elwise_mul_mut(ub.as_mut(), u.as_ref());
|
||||
ntt_op.backward(ub.as_mut());
|
||||
|
||||
// sample error
|
||||
rlwe_out.iter_rows_mut().for_each(|ri| {
|
||||
RandomGaussianDist::random_fill(rng, &q, ri.as_mut());
|
||||
});
|
||||
|
||||
// a*u + e0
|
||||
mod_op.elwise_add_mut(rlwe_out.get_row_mut(0), ua.as_ref());
|
||||
// b*u + e1
|
||||
mod_op.elwise_add_mut(rlwe_out.get_row_mut(1), ub.as_ref());
|
||||
|
||||
// b*u + e1 + m
|
||||
mod_op.elwise_add_mut(rlwe_out.get_row_mut(1), m);
|
||||
}
|
||||
|
||||
/// Generates RLWE public key
|
||||
pub(crate) fn gen_rlwe_public_key<
|
||||
Ro: RowMut + RowEntity,
|
||||
@@ -1778,19 +1854,20 @@ pub(crate) mod tests {
|
||||
carry_m[thread_rng().gen_range(0..ring_size) as usize] = 1;
|
||||
|
||||
// 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,
|
||||
);
|
||||
_pk_encrypt_rgsw(&carry_m, &public_key, &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..10 {
|
||||
for i in 0..100 {
|
||||
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);
|
||||
let rgsw_m = {
|
||||
RgswCiphertextEvaluationDomain::<_, DefaultSecureRng, NttBackendU64>::from(
|
||||
&_pk_encrypt_rgsw(&m, &public_key, &gadget_vector, &mod_op, &ntt_op),
|
||||
)
|
||||
};
|
||||
|
||||
rgsw_by_rgsw_inplace(
|
||||
&mut rgsw_carrym.data,
|
||||
@@ -1805,23 +1882,19 @@ pub(crate) mod tests {
|
||||
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);
|
||||
_measure_noise_rgsw(&rgsw_carrym.data, &carry_m, s.values(), &gadget_vector, q);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sk_rgsw_by_rgsw() {
|
||||
let logq = 60;
|
||||
let logq = 31;
|
||||
let logp = 2;
|
||||
let ring_size = 1 << 11;
|
||||
let ring_size = 1 << 10;
|
||||
let q = generate_prime(logq, ring_size, 1u64 << logq).unwrap();
|
||||
let p = 1u64 << logp;
|
||||
let d_rgsw = 15;
|
||||
let logb = 4;
|
||||
let d_rgsw = 4;
|
||||
let logb = 7;
|
||||
|
||||
let s = RlweSecret::random((ring_size >> 1) as usize, ring_size as usize);
|
||||
|
||||
@@ -1830,32 +1903,32 @@ pub(crate) mod tests {
|
||||
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 = |a: &u64, b: &u64| ((*a as u128 * *b 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;
|
||||
|
||||
// RGSW(carry_m)
|
||||
let mut rgsw_carrym =
|
||||
_sk_encrypt_rgsw(&carry_m, s.values(), &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
|
||||
};
|
||||
|
||||
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 {
|
||||
for i in 0..1000 {
|
||||
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
|
||||
};
|
||||
|
||||
let rgsw_m = _sk_encrypt_rgsw(&m, s.values(), &gadget_vector, &mod_op, &ntt_op);
|
||||
rgsw_by_rgsw_inplace(
|
||||
&mut rgsw_carrym.data,
|
||||
&rgsw_m,
|
||||
&mut rgsw_carrym,
|
||||
&rgsw_m.data,
|
||||
&decomposer,
|
||||
&mut scratch_matrix_d_plus_rgsw_by_ring,
|
||||
&ntt_op,
|
||||
@@ -1863,14 +1936,9 @@ 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 ###########");
|
||||
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);
|
||||
_measure_noise_rgsw(&rgsw_carrym, &carry_m, s.values(), &gadget_vector, q);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user