arnaucube
arnaucube/phantom-zonePublic Self-hosted repository
phantom-zone/src/multi_party.rs
src/multi_party.rsRaw
  1use std::fmt::Debug;
  2
  3use itertools::izip;
  4use num_traits::Zero;
  5
  6use crate::{
  7    backend::{GetModulus, Modulus, VectorOps},
  8    ntt::Ntt,
  9    random::{
 10        RandomFillGaussianInModulus, RandomFillUniformInModulus, RandomGaussianElementInModulus,
 11    },
 12    utils::TryConvertFrom1,
 13    ArithmeticOps, Matrix, MatrixEntity, MatrixMut, Row, RowEntity, RowMut,
 14};
 15
 16pub(crate) fn public_key_share<
 17    R: Row + RowMut + RowEntity,
 18    S,
 19    ModOp: VectorOps<Element = R::Element> + GetModulus<Element = R::Element>,
 20    NttOp: Ntt<Element = R::Element>,
 21    Rng: RandomFillGaussianInModulus<[R::Element], ModOp::M>,
 22    PRng: RandomFillUniformInModulus<[R::Element], ModOp::M>,
 23>(
 24    share_out: &mut R,
 25    s_i: &[S],
 26    modop: &ModOp,
 27    nttop: &NttOp,
 28    p_rng: &mut PRng,
 29    rng: &mut Rng,
 30) where
 31    R: TryConvertFrom1<[S], ModOp::M>,
 32{
 33    let ring_size = share_out.as_ref().len();
 34    assert!(s_i.len() == ring_size);
 35
 36    let q = modop.modulus();
 37
 38    // sample a
 39    let mut a = {
 40        let mut a = R::zeros(ring_size);
 41        RandomFillUniformInModulus::random_fill(p_rng, &q, a.as_mut());
 42        a
 43    };
 44
 45    // s*a
 46    nttop.forward(a.as_mut());
 47    let mut s = R::try_convert_from(s_i, &q);
 48    nttop.forward(s.as_mut());
 49    modop.elwise_mul_mut(s.as_mut(), a.as_ref());
 50    nttop.backward(s.as_mut());
 51
 52    RandomFillGaussianInModulus::random_fill(rng, &q, share_out.as_mut());
 53    modop.elwise_add_mut(share_out.as_mut(), s.as_ref()); // s*e + e
 54}
 55
 56/// Generate decryption share for LWE ciphertext `lwe_ct` with user's secret `s`
 57pub(crate) fn multi_party_decryption_share<
 58    R: RowMut + RowEntity,
 59    Mod: Modulus<Element = R::Element>,
 60    ModOp: ArithmeticOps<Element = R::Element> + VectorOps<Element = R::Element> + GetModulus<M = Mod>,
 61    Rng: RandomGaussianElementInModulus<R::Element, Mod>,
 62    S,
 63>(
 64    lwe_ct: &R,
 65    s: &[S],
 66    mod_op: &ModOp,
 67    rng: &mut Rng,
 68) -> R::Element
 69where
 70    R: TryConvertFrom1<[S], Mod>,
 71    R::Element: Zero,
 72{
 73    assert!(lwe_ct.as_ref().len() == s.len() + 1);
 74    let mut neg_s = R::try_convert_from(s, mod_op.modulus());
 75    mod_op.elwise_neg_mut(neg_s.as_mut());
 76
 77    // share =  (\sum -s_i * a_i) + e
 78    let mut share = R::Element::zero();
 79    izip!(neg_s.as_ref().iter(), lwe_ct.as_ref().iter().skip(1)).for_each(|(si, ai)| {
 80        share = mod_op.add(&share, &mod_op.mul(si, ai));
 81    });
 82
 83    let e = rng.random(mod_op.modulus());
 84    share = mod_op.add(&share, &e);
 85
 86    share
 87}
 88
 89/// Aggregate decryption shares for `lwe_ct` and return noisy decryption output
 90/// `m + e`
 91pub(crate) fn multi_party_aggregate_decryption_shares_and_decrypt<
 92    R: RowMut + RowEntity,
 93    ModOp: ArithmeticOps<Element = R::Element>,
 94>(
 95    lwe_ct: &R,
 96    shares: &[R::Element],
 97    mod_op: &ModOp,
 98) -> R::Element
 99where
100    R::Element: Zero,
101{
102    let mut sum_shares = R::Element::zero();
103    shares
104        .iter()
105        .for_each(|v| sum_shares = mod_op.add(&sum_shares, v));
106    mod_op.add(&lwe_ct.as_ref()[0], &sum_shares)
107}
108
109pub(crate) fn non_interactive_rgsw_ct<
110    M: MatrixMut + MatrixEntity,
111    S,
112    PRng: RandomFillUniformInModulus<[M::MatElement], ModOp::M>,
113    Rng: RandomFillGaussianInModulus<[M::MatElement], ModOp::M>,
114    NttOp: Ntt<Element = M::MatElement>,
115    ModOp: VectorOps<Element = M::MatElement> + GetModulus<Element = M::MatElement>,
116>(
117    s: &[S],
118    u: &[S],
119    m: &[M::MatElement],
120    gadget_vec: &[M::MatElement],
121    p_rng: &mut PRng,
122    rng: &mut Rng,
123    nttop: &NttOp,
124    modop: &ModOp,
125) -> (M, M)
126where
127    <M as Matrix>::R: RowMut + TryConvertFrom1<[S], ModOp::M> + RowEntity,
128    M::MatElement: Copy,
129{
130    assert_eq!(s.len(), u.len());
131    assert_eq!(s.len(), m.len());
132    let q = modop.modulus();
133    let d = gadget_vec.len();
134    let ring_size = s.len();
135
136    let mut s_poly_eval = M::R::try_convert_from(s, q);
137    let mut u_poly_eval = M::R::try_convert_from(u, q);
138    nttop.forward(s_poly_eval.as_mut());
139    nttop.forward(u_poly_eval.as_mut());
140
141    // encryptions of a_i*u + e + \beta m
142    let mut enc_beta_m = M::zeros(d, ring_size);
143    // zero encrypition: a_i*s + e'
144    let mut zero_encryptions = M::zeros(d, ring_size);
145
146    let mut scratch_space = M::R::zeros(ring_size);
147
148    izip!(
149        enc_beta_m.iter_rows_mut(),
150        zero_encryptions.iter_rows_mut(),
151        gadget_vec.iter()
152    )
153    .for_each(|(e_beta_m, e_zero, beta)| {
154        // sample a_i
155        RandomFillUniformInModulus::random_fill(p_rng, q, e_beta_m.as_mut());
156        e_zero.as_mut().copy_from_slice(e_beta_m.as_ref());
157
158        // a_i * u + \beta m + e //
159        // a_i * u
160        nttop.forward(e_beta_m.as_mut());
161        modop.elwise_mul_mut(e_beta_m.as_mut(), u_poly_eval.as_ref());
162        nttop.backward(e_beta_m.as_mut());
163        // sample error e
164        RandomFillGaussianInModulus::random_fill(rng, q, scratch_space.as_mut());
165        // a_i * u + e
166        modop.elwise_add_mut(e_beta_m.as_mut(), scratch_space.as_ref());
167        // beta * m
168        modop.elwise_scalar_mul(scratch_space.as_mut(), m.as_ref(), beta);
169        // a_i * u + e + \beta m
170        modop.elwise_add_mut(e_beta_m.as_mut(), scratch_space.as_ref());
171
172        // a_i * s + e //
173        // a_i * s
174        nttop.forward(e_zero.as_mut());
175        modop.elwise_mul_mut(e_zero.as_mut(), s_poly_eval.as_ref());
176        nttop.backward(e_zero.as_mut());
177        // sample error e
178        RandomFillGaussianInModulus::random_fill(rng, q, scratch_space.as_mut());
179        // a_i * s + e
180        modop.elwise_add_mut(e_zero.as_mut(), scratch_space.as_ref());
181    });
182
183    (enc_beta_m, zero_encryptions)
184}
185
186pub(crate) fn non_interactive_ksk_gen<
187    M: MatrixMut + MatrixEntity,
188    S,
189    PRng: RandomFillUniformInModulus<[M::MatElement], ModOp::M>,
190    Rng: RandomFillGaussianInModulus<[M::MatElement], ModOp::M>,
191    NttOp: Ntt<Element = M::MatElement>,
192    ModOp: VectorOps<Element = M::MatElement> + GetModulus<Element = M::MatElement>,
193>(
194    s: &[S],
195    u: &[S],
196    gadget_vec: &[M::MatElement],
197    p_rng: &mut PRng,
198    rng: &mut Rng,
199    nttop: &NttOp,
200    modop: &ModOp,
201) -> M
202where
203    <M as Matrix>::R: RowMut + TryConvertFrom1<[S], ModOp::M> + RowEntity,
204    M::MatElement: Copy + Debug,
205{
206    assert_eq!(s.len(), u.len());
207
208    let q = modop.modulus();
209    let d = gadget_vec.len();
210    let ring_size = s.len();
211
212    let mut s_poly_eval = M::R::try_convert_from(s, q);
213    nttop.forward(s_poly_eval.as_mut());
214    let u_poly = M::R::try_convert_from(u, q);
215    // a_i * s + \beta u + e
216    let mut ksk = M::zeros(d, ring_size);
217
218    let mut scratch_space = M::R::zeros(ring_size);
219
220    izip!(ksk.iter_rows_mut(), gadget_vec.iter()).for_each(|(e_ksk, beta)| {
221        // sample a_i
222        RandomFillUniformInModulus::random_fill(p_rng, q, e_ksk.as_mut());
223
224        // a_i * s + e + beta u
225        nttop.forward(e_ksk.as_mut());
226        modop.elwise_mul_mut(e_ksk.as_mut(), s_poly_eval.as_ref());
227        nttop.backward(e_ksk.as_mut());
228        // sample error e
229        RandomFillGaussianInModulus::random_fill(rng, q, scratch_space.as_mut());
230        // a_i * s + e
231        modop.elwise_add_mut(e_ksk.as_mut(), scratch_space.as_ref());
232        // \beta * u
233        modop.elwise_scalar_mul(scratch_space.as_mut(), u_poly.as_ref(), beta);
234        // a_i * s + e + \beta * u
235        modop.elwise_add_mut(e_ksk.as_mut(), scratch_space.as_ref());
236    });
237
238    ksk
239}
240
241pub(crate) fn non_interactive_ksk_zero_encryptions_for_other_party_i<
242    M: MatrixMut + MatrixEntity,
243    S,
244    PRng: RandomFillUniformInModulus<[M::MatElement], ModOp::M>,
245    Rng: RandomFillGaussianInModulus<[M::MatElement], ModOp::M>,
246    NttOp: Ntt<Element = M::MatElement>,
247    ModOp: VectorOps<Element = M::MatElement> + GetModulus<Element = M::MatElement>,
248>(
249    s: &[S],
250    gadget_vec: &[M::MatElement],
251    p_rng: &mut PRng,
252    rng: &mut Rng,
253    nttop: &NttOp,
254    modop: &ModOp,
255) -> M
256where
257    <M as Matrix>::R: RowMut + TryConvertFrom1<[S], ModOp::M> + RowEntity,
258    M::MatElement: Copy + Debug,
259{
260    let q = modop.modulus();
261    let d = gadget_vec.len();
262    let ring_size = s.len();
263
264    let mut s_poly_eval = M::R::try_convert_from(s, q);
265    nttop.forward(s_poly_eval.as_mut());
266
267    // a_i * s + e
268    let mut zero_encs = M::zeros(d, ring_size);
269
270    let mut scratch_space = M::R::zeros(ring_size);
271
272    izip!(zero_encs.iter_rows_mut()).for_each(|e_zero| {
273        // sample a_i
274        RandomFillUniformInModulus::random_fill(p_rng, q, e_zero.as_mut());
275
276        // a_i * s + e
277        nttop.forward(e_zero.as_mut());
278        modop.elwise_mul_mut(e_zero.as_mut(), s_poly_eval.as_ref());
279        nttop.backward(e_zero.as_mut());
280        // sample error e
281        RandomFillGaussianInModulus::random_fill(rng, q, scratch_space.as_mut());
282        modop.elwise_add_mut(e_zero.as_mut(), scratch_space.as_ref());
283    });
284
285    zero_encs
286}