Browse Source

clean rgsw/runtime

par-agg-key-shares
Janmajaya Mall 9 months ago
parent
commit
71901378b0
5 changed files with 90 additions and 125 deletions
  1. +2
    -2
      src/bool/evaluator.rs
  2. +2
    -2
      src/bool/ni_mp_api.rs
  3. +2
    -2
      src/bool/print_noise.rs
  4. +10
    -80
      src/rgsw/mod.rs
  5. +74
    -39
      src/rgsw/runtime.rs

+ 2
- 2
src/bool/evaluator.rs

@ -25,8 +25,8 @@ use crate::{
RandomFillUniformInModulus, RandomGaussianElementInModulus,
},
rgsw::{
decrypt_rlwe, galois_auto, generate_auto_map, public_key_encrypt_rgsw,
rgsw_by_rgsw_inplace, secret_key_encrypt_rgsw, seeded_auto_key_gen,
decrypt_rlwe, generate_auto_map, public_key_encrypt_rgsw, rgsw_by_rgsw_inplace, rlwe_auto,
secret_key_encrypt_rgsw, seeded_auto_key_gen,
},
utils::{
encode_x_pow_si_with_emebedding_factor, fill_random_ternary_secret_with_hamming_weight,

+ 2
- 2
src/bool/ni_mp_api.rs

@ -178,7 +178,7 @@ mod impl_enc_dec {
bool::{evaluator::BoolEncoding, keys::NonInteractiveMultiPartyClientKey},
pbs::{sample_extract, PbsInfo, WithShoupRepr},
random::{NewWithSeed, RandomFillUniformInModulus},
rgsw::{key_switch, seeded_secret_key_encrypt_rlwe},
rgsw::{rlwe_key_switch, seeded_secret_key_encrypt_rlwe},
utils::TryConvertFrom1,
Encryptor, KeySwitchWithId, Matrix, MatrixEntity, MatrixMut, RowEntity, RowMut,
};
@ -324,7 +324,7 @@ mod impl_enc_dec {
let decomposer = e.ni_ui_to_s_ks_decomposer().as_ref().unwrap();
// perform key switch
key_switch(
rlwe_key_switch(
self,
ksk.as_ref(),
ksk.shoup_repr(),

+ 2
- 2
src/bool/print_noise.rs

@ -10,7 +10,7 @@ use crate::{
lwe::{decrypt_lwe, lwe_key_switch},
parameters::{BoolParameters, CiphertextModulus},
random::{DefaultSecureRng, RandomFillUniformInModulus},
rgsw::{decrypt_rlwe, galois_auto, IsTrivial, RlweCiphertext},
rgsw::{decrypt_rlwe, rlwe_auto, IsTrivial, RlweCiphertext},
utils::{encode_x_pow_si_with_emebedding_factor, tests::Stats, TryConvertFrom1},
ArithmeticOps, ClientKey, Decomposer, MatrixEntity, MatrixMut, ModInit, Ntt, NttInit,
RowEntity, RowMut, VectorOps,
@ -283,7 +283,7 @@ where
rlwe.data.get_row_mut(0).copy_from_slice(m.as_ref());
rlwe.set_not_trivial();
galois_auto(
rlwe_auto(
&mut rlwe,
server_key.galois_key_for_auto(*k),
&mut scratch_matrix,

+ 10
- 80
src/rgsw/mod.rs

@ -9,14 +9,11 @@ use std::{
};
use crate::{
backend::{ArithmeticOps, GetModulus, Modulus, VectorOps},
decomposer::{self, Decomposer, RlweDecomposer},
ntt::{self, Ntt, NttInit},
random::{
DefaultSecureRng, NewWithSeed, RandomElementInModulus, RandomFill,
RandomFillGaussianInModulus, RandomFillUniformInModulus,
},
utils::{fill_random_ternary_secret_with_hamming_weight, ToShoup, TryConvertFrom1, WithLocal},
backend::Modulus,
decomposer::{Decomposer, RlweDecomposer},
ntt::{Ntt, NttInit},
random::{DefaultSecureRng, NewWithSeed, RandomFillUniformInModulus},
utils::{fill_random_ternary_secret_with_hamming_weight, ToShoup, WithLocal},
Matrix, MatrixEntity, MatrixMut, Row, RowEntity, RowMut, Secret,
};
@ -210,8 +207,6 @@ where
});
// sample A polynomials of RLWE'(m) - RLWE'A(m)
// TODO(Jay): Do we want to be generic over RandomGenerator used here? I think
// not.
let mut p_rng = R::new_with_seed(value.seed.clone());
izip!(data.iter_rows_mut().skip(value.d_a * 2).take(value.d_b * 1))
.for_each(|ri| p_rng.random_fill(&value.modulus, ri.as_mut()));
@ -528,7 +523,7 @@ pub(crate) mod tests {
rlwe_public_key, secret_key_encrypt_rgsw, seeded_auto_key_gen,
seeded_secret_key_encrypt_rlwe,
},
runtime::{galois_auto, rgsw_by_rgsw_inplace, rlwe_by_rgsw},
runtime::{rgsw_by_rgsw_inplace, rlwe_auto, rlwe_by_rgsw},
AutoKeyEvaluationDomain, RgswCiphertext, RgswCiphertextEvaluationDomain, RlweCiphertext,
RlwePublicKey, RlweSecret, SeededAutoKey, SeededRgswCiphertext, SeededRlweCiphertext,
SeededRlwePublicKey,
@ -636,69 +631,6 @@ pub(crate) mod tests {
seeded_rgsw_ct
}
/// Prints noise in RGSW ciphertext RGSW(m).
///
/// - rgsw_ct: RGSW ciphertext in coefficient domain
pub(crate) fn _measure_noise_rgsw<T: Modulus<Element = u64> + Clone>(
rgsw_ct: &[Vec<u64>],
m: &[u64],
s: &[i32],
decomposer: &(DefaultDecomposer<u64>, DefaultDecomposer<u64>),
q: &T,
) {
let gadget_vector_a = decomposer.a().gadget_vector();
let gadget_vector_b = decomposer.b().gadget_vector();
let d_a = gadget_vector_a.len();
let d_b = gadget_vector_b.len();
let ring_size = s.len();
assert!(Matrix::dimension(&rgsw_ct) == (d_a * 2 + d_b * 2, ring_size));
assert!(m.len() == ring_size);
let mod_op = ModularOpsU64::new(q.clone());
let ntt_op = NttBackendU64::new(q, ring_size);
let mul_mod =
|a: &u64, b: &u64| ((*a as u128 * *b as u128) % q.q().unwrap() as u128) as u64;
let s_poly = Vec::<u64>::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.q().unwrap());
// RLWE(\beta^j -s * m)
for j in 0..d_a {
let ideal_m = {
// 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_a[j]);
beta_neg_sm0m1
};
let mut rlwe = vec![vec![0u64; ring_size as usize]; 2];
rlwe[0].copy_from_slice(rgsw_ct.get_row_slice(j));
rlwe[1].copy_from_slice(rgsw_ct.get_row_slice(d_a + j));
let noise = measure_noise(&rlwe, &ideal_m, &ntt_op, &mod_op, s);
println!(r"Noise RLWE(\beta^{j} -sm0m1): {noise}");
}
// RLWE(\beta^j m)
for j in 0..d_b {
let ideal_m = {
// 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_b[j]);
beta_m0m1
};
let mut rlwe = vec![vec![0u64; ring_size as usize]; 2];
rlwe[0].copy_from_slice(rgsw_ct.get_row_slice(d_a * 2 + j));
rlwe[1].copy_from_slice(rgsw_ct.get_row_slice(d_a * 2 + d_b + j));
let noise = measure_noise(&rlwe, &ideal_m, &ntt_op, &mod_op, s);
println!(r"Noise RLWE(\beta^{j} m0m1): {noise}");
}
}
#[test]
fn rlwe_encrypt_decryption() {
let logq = 50;
@ -742,9 +674,6 @@ 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]
@ -999,7 +928,7 @@ pub(crate) mod tests {
// normal galois auto
{
galois_auto(
rlwe_auto(
&mut rlwe_m,
&auto_key.data,
&mut scratch_space,
@ -1104,7 +1033,7 @@ pub(crate) mod tests {
)
];
_measure_noise_rgsw(&rgsw_carrym, &carry_m, s.values(), &decomposer, &q);
// _measure_noise_rgsw(&rgsw_carrym, &carry_m, s.values(), &decomposer, &q);
for i in 0..2 {
let mut m = vec![0u64; ring_size as usize];
@ -1127,7 +1056,8 @@ pub(crate) mod tests {
// measure noise
carry_m = negacyclic_mul(&carry_m, &m, mul_mod, q);
println!("########### Noise RGSW(carrym) in {i}^th loop ###########");
_measure_noise_rgsw(&rgsw_carrym, &carry_m, s.values(), &decomposer, &q);
// _measure_noise_rgsw(&rgsw_carrym, &carry_m, s.values(),
// &decomposer, &q);
}
{
// RLWE(m) x RGSW(carry_m)

+ 74
- 39
src/rgsw/runtime.rs

@ -10,7 +10,7 @@ use crate::{
use super::IsTrivial;
pub(crate) fn routine<R: RowMut, ModOp: VectorOps<Element = R::Element>>(
pub(crate) fn poly_fma_routine<R: RowMut, ModOp: VectorOps<Element = R::Element>>(
write_to_row: &mut [R::Element],
matrix_a: &[R],
matrix_b: &[R],
@ -48,11 +48,18 @@ pub(crate) fn decompose_r>(
}
}
/// Sends RLWE_{s}(X) -> RLWE_{s}(X^k) where k is some galois element
/// Sends RLWE_{s(X)}(m(X)) -> RLWE_{s(X)}(m{X^k}) where k is some galois
/// element
///
/// - scratch_matrix: must have dimension at-least d+2 x ring_size. d rows to
/// store decomposed polynomials and 2 for rlwe
pub(crate) fn galois_auto<
/// - rlwe_in: Input ciphertext RLWE_{s(X)}(m(X)).
/// - ksk: Auto key switching key with polynomials in evaluation domain
/// - auto_map_index: If automorphism sends i^th coefficient of m(X) to j^th
/// coefficient of m(X^k) then auto_map_index[i] = j
/// - auto_sign_index: With a = m(X)[i], if m(X^k)[auto_map_index[i]] = -a, then
/// auto_sign_index[i] = false, else auto_sign_index[i] = true
/// - scratch_matrix: must have dimension at-least d+2 x ring_size. `d` rows to
/// store decomposed polynomials nad 2 rows to store out RLWE temporarily.
pub(crate) fn rlwe_auto<
MT: Matrix + IsTrivial + MatrixMut,
Mmut: MatrixMut<MatElement = MT::MatElement>,
ModOp: ArithmeticOps<Element = MT::MatElement> + VectorOps<Element = MT::MatElement>,
@ -119,7 +126,7 @@ pub(crate) fn galois_auto<
// key switch: (a * RLWE'(s(X^k)))
let (ksk_a, ksk_b) = ksk.split_at_row(d);
// a' = decomp<a> * RLWE'_A(s(X^k))
routine(
poly_fma_routine(
tmp_rlwe_out[0].as_mut(),
scratch_matrix_d_ring,
ksk_a,
@ -127,7 +134,7 @@ pub(crate) fn galois_auto<
);
// b' += decomp<a(X^k)> * RLWE'_B(s(X^k))
routine(
poly_fma_routine(
tmp_rlwe_out[1].as_mut(),
scratch_matrix_d_ring,
ksk_b,
@ -181,6 +188,12 @@ pub(crate) fn galois_auto<
.copy_from_slice(tmp_rlwe_out[1].as_ref());
}
/// Sends RLWE_{s(X)}(m(X)) -> RLWE_{s(X)}(m{X^k}) where k is some galois
/// element
///
/// This is same as `galois_auto` with the difference that alongside `ksk` with
/// key switching polynomials in evaluation domain, shoup representation,
/// `ksk_shoup`, of the polynomials in evaluation domain is also supplied.
pub(crate) fn galois_auto_shoup<
MT: Matrix + IsTrivial + MatrixMut,
Mmut: MatrixMut<MatElement = MT::MatElement>,
@ -309,14 +322,12 @@ pub(crate) fn galois_auto_shoup<
.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)
/// Inplace mutates RLWE(m0) to equal RLWE(m0m1) = RLWE(m0) x RGSW(m1).
///
/// - 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 with atleast max(d_a, d_b) rows and
/// ring_size columns. It's used to store decomposed polynomials and out RLWE
/// temoporarily
/// - scratch_matrix: with dimension (max(d_a, d_b) + 2) x ring_size columns.
/// It's used to store decomposed polynomials and out RLWE temporarily
pub(crate) fn rlwe_by_rgsw<
Mmut: MatrixMut,
MT: Matrix<MatElement = Mmut::MatElement> + MatrixMut<MatElement = Mmut::MatElement> + IsTrivial,
@ -365,14 +376,14 @@ pub(crate) fn rlwe_by_rgsw<
.take(d_a)
.for_each(|r| ntt_op.forward(r.as_mut()));
// a_out += decomp<a_in> \cdot RLWE_A'(-sm)
routine(
poly_fma_routine(
scratch_rlwe_out[0].as_mut(),
&scratch_matrix_d_ring[..d_a],
&rlwe_dash_nsm[..d_a],
mod_op,
);
// b_out += decomp<a_in> \cdot RLWE_B'(-sm)
routine(
poly_fma_routine(
scratch_rlwe_out[1].as_mut(),
&scratch_matrix_d_ring[..d_a],
&rlwe_dash_nsm[d_a..],
@ -390,14 +401,14 @@ pub(crate) fn rlwe_by_rgsw<
.take(d_b)
.for_each(|r| ntt_op.forward(r.as_mut()));
// a_out += decomp<b_in> \cdot RLWE_A'(m)
routine(
poly_fma_routine(
scratch_rlwe_out[0].as_mut(),
&scratch_matrix_d_ring[..d_b],
&rlwe_dash_m[..d_b],
mod_op,
);
// b_out += decomp<b_in> \cdot RLWE_B'(m)
routine(
poly_fma_routine(
scratch_rlwe_out[1].as_mut(),
&scratch_matrix_d_ring[..d_b],
&rlwe_dash_m[d_b..],
@ -418,6 +429,11 @@ pub(crate) fn rlwe_by_rgsw<
rlwe_in.set_not_trivial();
}
/// Inplace mutates RLWE(m0) to equal RLWE(m0m1) = RLWE(m0) x RGSW(m1).
///
/// Same as `rlwe_by_rgsw` with the difference that alongside `rgsw_in` with
/// polynomials in evaluation domain, shoup representation of polynomials in
/// evaluation domain, `rgsw_in_shoup`, is also supplied.
pub(crate) fn rlwe_by_rgsw_shoup<
Mmut: MatrixMut,
MT: Matrix<MatElement = Mmut::MatElement> + MatrixMut<MatElement = Mmut::MatElement> + IsTrivial,
@ -528,27 +544,32 @@ pub(crate) fn rlwe_by_rgsw_shoup<
rlwe_in.set_not_trivial();
}
/// Inplace mutates rlwe_0 to equal RGSW(m0m1) = RGSW(m0)xRGSW(m1)
/// in evaluation domain
/// Inplace mutates RGSW(m0) to equal RGSW(m0m1) = RGSW(m0)xRGSW(m1)
///
/// RGSW x RGSW product requires multiple RLWE x RGSW products. For example,
/// Define
///
/// RGSW(m0) = [RLWE(-sm), RLWE(\beta -sm), ..., RLWE(\beta^{d-1} -sm)
/// RLWE(m), RLWE(\beta m), ..., RLWE(\beta^{d-1} m)]
/// And 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.
/// Then RGSW(m0) x RGSW(m1) equals:
/// RGSW(m0m1) = [
/// rlwe_x_rgsw(RLWE(-sm), RGSW(m1)),
/// ...,
/// rlwe_x_rgsw(RLWE(\beta^{d-1} -sm), RGSW(m1)),
/// rlwe_x_rgsw(RLWE(m), RGSW(m1)),
/// ...,
/// rlwe_x_rgsw(RLWE(\beta^{d-1} m), RGSW(m1)),
/// ]
///
/// - rgsw_0: RGSW(m0)
/// - rgsw_1_eval: RGSW(m1) in Evaluation domain
/// - scratch_matrix_d_plus_rgsw_by_ring: scratch space matrix with rows
/// (max(d_a, d_b) + d_a*2+d_b*2) and columns ring_size
/// Since noise growth in RLWE x RGSW depends on noise in RGSW ciphertext, it is
/// clear to observe from above that noise in resulting RGSW(m0m1) equals noise
/// accumulated in a single RLWE x RGSW and depends on noise in RGSW(m1) (i.e.
/// rgsw_1_eval)
///
/// ## Note:
/// - We treat RGSW x RGSW as multiple RLWE x RGSW multiplications. .
/// - rgsw_0: RGSW(m0) in coefficient domain
/// - rgsw_1_eval: RGSW(m1) in evaluation domain
pub(crate) fn rgsw_by_rgsw_inplace<
Mmut: MatrixMut,
D: RlweDecomposer<Element = Mmut::MatElement>,
@ -620,13 +641,13 @@ pub(crate) fn rgsw_by_rgsw_inplace<
.iter_mut()
.take(d_a)
.for_each(|ri| ntt_op.forward(ri.as_mut()));
routine(
poly_fma_routine(
rlwe_out_a.as_mut(),
&decomp_r_space[..d_a],
&rgsw1_nsm[..d_a],
mod_op,
);
routine(
poly_fma_routine(
rlwe_out_b.as_mut(),
&decomp_r_space[..d_a],
&rgsw1_nsm[d_a..],
@ -639,13 +660,13 @@ pub(crate) fn rgsw_by_rgsw_inplace<
.iter_mut()
.take(d_b)
.for_each(|ri| ntt_op.forward(ri.as_mut()));
routine(
poly_fma_routine(
rlwe_out_a.as_mut(),
&decomp_r_space[..d_b],
&rgsw1_m[..d_b],
mod_op,
);
routine(
poly_fma_routine(
rlwe_out_b.as_mut(),
&decomp_r_space[..d_b],
&rgsw1_m[d_b..],
@ -663,7 +684,21 @@ pub(crate) fn rgsw_by_rgsw_inplace<
.for_each(|ri| ntt_op.backward(ri.as_mut()));
}
pub(crate) fn key_switch<
/// Key switches input RLWE_{s'}(m) -> RLWE_{s}(m)
///
/// Let RLWE_{s'}(m) = [a, b] s.t. m+e = b - as'
///
/// Given key switchin key Ksk(s' -> s) = RLWE'_{s}(s') = [RLWE_{s}(beta^i s')]
/// = [a, a*s + e + beta^i s'] for i \in [0,d), key switching computes:
/// 1. RLWE_{s}(-s'a) = \sum signed_decompose(-a)[i] RLWE_{s}(beta^i s')
/// 2. RLWE_{s}(m) = (b, 0) + RLWE_{s}(-s'a)
///
/// - rlwe_in: Input rlwe ciphertext
/// - ksk: Key switching key Ksk(s' -> s) with polynomials in evaluation domain
/// - ksk_shoup: Key switching key Ksk(s' -> s) with polynomials in evaluation
/// domain in shoup representation
/// - decomposer: Decomposer used for key switching
pub(crate) fn rlwe_key_switch<
M: MatrixMut + MatrixEntity,
ModOp: GetModulus<Element = M::MatElement> + ShoupMatrixFMA<M::R> + VectorOps<Element = M::MatElement>,
NttOp: Ntt<Element = M::MatElement>,

Loading…
Cancel
Save