mirror of
https://github.com/arnaucube/phantom-zone.git
synced 2026-01-08 23:21:29 +01:00
add NonInteractiveBatchedFheUInt8s for non-interactive MPC; Make multi-party decyrption protocol independent of bool evaluator
This commit is contained in:
@@ -2070,41 +2070,6 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn multi_party_decryption_share<K: InteractiveMultiPartyClientKey<Element = i32>>(
|
||||
&self,
|
||||
lwe_ct: &M::R,
|
||||
client_key: &K,
|
||||
) -> <M as Matrix>::MatElement {
|
||||
assert!(lwe_ct.as_ref().len() == self.pbs_info.parameters.rlwe_n().0 + 1);
|
||||
let modop = &self.pbs_info.rlwe_modop;
|
||||
let mut neg_s =
|
||||
M::R::try_convert_from(&client_key.sk_rlwe(), &self.pbs_info.parameters.rlwe_q());
|
||||
modop.elwise_neg_mut(neg_s.as_mut());
|
||||
|
||||
let mut neg_sa = M::MatElement::zero();
|
||||
izip!(lwe_ct.as_ref().iter().skip(1), neg_s.as_ref().iter()).for_each(|(ai, nsi)| {
|
||||
neg_sa = modop.add(&neg_sa, &modop.mul(ai, nsi));
|
||||
});
|
||||
|
||||
let e = DefaultSecureRng::with_local_mut(|rng| {
|
||||
RandomGaussianElementInModulus::random(rng, self.pbs_info.parameters.rlwe_q())
|
||||
});
|
||||
let share = modop.add(&neg_sa, &e);
|
||||
|
||||
share
|
||||
}
|
||||
|
||||
pub(crate) fn multi_party_decrypt(&self, shares: &[M::MatElement], lwe_ct: &M::R) -> bool {
|
||||
let modop = &self.pbs_info.rlwe_modop;
|
||||
let mut sum_a = M::MatElement::zero();
|
||||
shares
|
||||
.iter()
|
||||
.for_each(|share_i| sum_a = modop.add(&sum_a, &share_i));
|
||||
|
||||
let encoded_m = modop.add(&lwe_ct.as_ref()[0], &sum_a);
|
||||
self.pbs_info.parameters.rlwe_q().decode(encoded_m)
|
||||
}
|
||||
|
||||
pub fn sk_encrypt<K: SinglePartyClientKey<Element = i32>>(
|
||||
&self,
|
||||
m: bool,
|
||||
|
||||
@@ -99,6 +99,7 @@ mod impl_ck {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "interactive_mp")]
|
||||
impl<E> InteractiveMultiPartyClientKey for ClientKey<[u8; 32], E> {
|
||||
type Element = i32;
|
||||
fn sk_lwe(&self) -> Vec<Self::Element> {
|
||||
@@ -109,6 +110,7 @@ mod impl_ck {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "non_interactive_mp")]
|
||||
impl<E> NonInteractiveMultiPartyClientKey for ClientKey<[u8; 32], E> {
|
||||
type Element = i32;
|
||||
fn sk_lwe(&self) -> Vec<Self::Element> {
|
||||
|
||||
@@ -205,25 +205,6 @@ mod common_mp_enc_dec {
|
||||
|
||||
type Mat = Vec<Vec<u64>>;
|
||||
|
||||
impl<E> MultiPartyDecryptor<bool, <Mat as Matrix>::R> for super::keys::ClientKey<[u8; 32], E> {
|
||||
type DecryptionShare = <Mat as Matrix>::MatElement;
|
||||
|
||||
/// Generate multi-party decryption share for LWE ciphertext `c`
|
||||
fn gen_decryption_share(&self, c: &<Mat as Matrix>::R) -> Self::DecryptionShare {
|
||||
BoolEvaluator::with_local(|e| e.multi_party_decryption_share(c, self))
|
||||
}
|
||||
|
||||
/// Aggregate mult-party decryptions shares of all parties, decrypt LWE
|
||||
/// ciphertext `c`, and return the bool plaintext
|
||||
fn aggregate_decryption_shares(
|
||||
&self,
|
||||
c: &<Mat as Matrix>::R,
|
||||
shares: &[Self::DecryptionShare],
|
||||
) -> bool {
|
||||
BoolEvaluator::with_local(|e| e.multi_party_decrypt(shares, c))
|
||||
}
|
||||
}
|
||||
|
||||
impl SampleExtractor<<Mat as Matrix>::R> for Mat {
|
||||
/// Sample extract coefficient at `index` as a LWE ciphertext from RLWE
|
||||
/// ciphertext `Self`
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{cell::RefCell, ops::Mul, sync::OnceLock};
|
||||
use std::{cell::RefCell, sync::OnceLock};
|
||||
|
||||
use crate::{
|
||||
backend::{ModularOpsU64, ModulusPowerOf2},
|
||||
@@ -184,9 +184,13 @@ impl Global for RuntimeServerKey {
|
||||
mod impl_enc_dec {
|
||||
use crate::{
|
||||
bool::evaluator::BoolEncoding,
|
||||
multi_party::{
|
||||
multi_party_aggregate_decryption_shares_and_decrypt, multi_party_decryption_share,
|
||||
},
|
||||
pbs::{sample_extract, PbsInfo},
|
||||
rgsw::public_key_encrypt_rlwe,
|
||||
Encryptor, Matrix, MatrixEntity, RowEntity,
|
||||
utils::TryConvertFrom1,
|
||||
Encryptor, Matrix, MatrixEntity, MultiPartyDecryptor, RowEntity,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use num_traits::{ToPrimitive, Zero};
|
||||
@@ -254,14 +258,50 @@ mod impl_enc_dec {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<K> MultiPartyDecryptor<bool, <Mat as Matrix>::R> for K
|
||||
where
|
||||
K: InteractiveMultiPartyClientKey,
|
||||
<Mat as Matrix>::R:
|
||||
TryConvertFrom1<[K::Element], CiphertextModulus<<Mat as Matrix>::MatElement>>,
|
||||
{
|
||||
type DecryptionShare = <Mat as Matrix>::MatElement;
|
||||
|
||||
fn gen_decryption_share(&self, c: &<Mat as Matrix>::R) -> Self::DecryptionShare {
|
||||
BoolEvaluator::with_local(|e| {
|
||||
DefaultSecureRng::with_local_mut(|rng| {
|
||||
multi_party_decryption_share(
|
||||
c,
|
||||
self.sk_rlwe().as_slice(),
|
||||
e.pbs_info().modop_rlweq(),
|
||||
rng,
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn aggregate_decryption_shares(
|
||||
&self,
|
||||
c: &<Mat as Matrix>::R,
|
||||
shares: &[Self::DecryptionShare],
|
||||
) -> bool {
|
||||
BoolEvaluator::with_local(|e| {
|
||||
let noisy_m = multi_party_aggregate_decryption_shares_and_decrypt(
|
||||
c,
|
||||
shares,
|
||||
e.pbs_info().modop_rlweq(),
|
||||
);
|
||||
|
||||
e.pbs_info().rlwe_q().decode(noisy_m)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::thread::panicking;
|
||||
|
||||
use itertools::Itertools;
|
||||
use rand::{thread_rng, RngCore};
|
||||
use rand::{thread_rng, Rng, RngCore};
|
||||
|
||||
use crate::{
|
||||
bool::{
|
||||
@@ -269,7 +309,7 @@ mod tests {
|
||||
keys::tests::{ideal_sk_rlwe, measure_noise_lwe},
|
||||
BooleanGates,
|
||||
},
|
||||
Encryptor, MultiPartyDecryptor,
|
||||
Encryptor, MultiPartyDecryptor, SampleExtractor,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
@@ -363,13 +403,52 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batched_fhe_u8s_extract_works() {
|
||||
set_parameter_set(ParameterSelector::InteractiveLTE2Party);
|
||||
let mut seed = [0u8; 32];
|
||||
thread_rng().fill_bytes(&mut seed);
|
||||
set_mp_seed(seed);
|
||||
|
||||
let parties = 2;
|
||||
let cks = (0..parties).map(|_| gen_client_key()).collect_vec();
|
||||
|
||||
// round 1
|
||||
let pk_shares = cks.iter().map(|k| gen_mp_keys_phase1(k)).collect_vec();
|
||||
|
||||
// collective pk
|
||||
let pk = aggregate_public_key_shares(&pk_shares);
|
||||
|
||||
let parameters = BoolEvaluator::with_local(|e| e.parameters().clone());
|
||||
|
||||
let batch_size = parameters.rlwe_n().0 * 3 + 123;
|
||||
let m = (0..batch_size)
|
||||
.map(|_| thread_rng().gen::<u8>())
|
||||
.collect_vec();
|
||||
|
||||
let seeded_ct = pk.encrypt(m.as_slice());
|
||||
|
||||
let m_back = (0..batch_size)
|
||||
.map(|i| {
|
||||
let ct = seeded_ct.extract_at(i);
|
||||
cks[0].aggregate_decryption_shares(
|
||||
&ct,
|
||||
&cks.iter()
|
||||
.map(|k| k.gen_decryption_share(&ct))
|
||||
.collect_vec(),
|
||||
)
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
assert_eq!(m, m_back);
|
||||
}
|
||||
|
||||
mod sp_api {
|
||||
use num_traits::ToPrimitive;
|
||||
use rand::Rng;
|
||||
|
||||
use crate::{
|
||||
bool::impl_bool_frontend::FheBool, pbs::PbsInfo, rgsw::seeded_secret_key_encrypt_rlwe,
|
||||
Decryptor, SampleExtractor,
|
||||
Decryptor,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
@@ -501,28 +580,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_extract_works() {
|
||||
set_single_party_parameter_sets(SP_TEST_BOOL_PARAMS);
|
||||
|
||||
let (ck, sk) = gen_keys();
|
||||
sk.set_server_key();
|
||||
|
||||
let batch_size = (SP_TEST_BOOL_PARAMS.rlwe_n().0 * 3 + 123);
|
||||
let m = (0..batch_size)
|
||||
.map(|_| thread_rng().gen::<u8>())
|
||||
.collect_vec();
|
||||
|
||||
let seeded_ct = ck.encrypt(m.as_slice());
|
||||
let ct = seeded_ct.unseed::<Vec<Vec<u64>>>();
|
||||
|
||||
let m_back = (0..batch_size)
|
||||
.map(|i| ck.decrypt(&ct.extract_at(i)))
|
||||
.collect_vec();
|
||||
|
||||
assert_eq!(m, m_back);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "interactive_mp")]
|
||||
fn all_uint8_apis() {
|
||||
|
||||
@@ -200,11 +200,15 @@ pub(super) struct BatchedFheBools<C> {
|
||||
mod impl_enc_dec {
|
||||
use crate::{
|
||||
bool::{evaluator::BoolEncoding, keys::NonInteractiveMultiPartyClientKey},
|
||||
multi_party::{
|
||||
multi_party_aggregate_decryption_shares_and_decrypt, multi_party_decryption_share,
|
||||
},
|
||||
pbs::{sample_extract, PbsInfo, WithShoupRepr},
|
||||
random::{NewWithSeed, RandomFillUniformInModulus},
|
||||
rgsw::{rlwe_key_switch, seeded_secret_key_encrypt_rlwe},
|
||||
utils::TryConvertFrom1,
|
||||
Encryptor, KeySwitchWithId, Matrix, MatrixEntity, MatrixMut, RowEntity, RowMut,
|
||||
Encryptor, KeySwitchWithId, Matrix, MatrixEntity, MatrixMut, MultiPartyDecryptor,
|
||||
RowEntity, RowMut,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use num_traits::{ToPrimitive, Zero};
|
||||
@@ -351,6 +355,44 @@ mod impl_enc_dec {
|
||||
}
|
||||
}
|
||||
|
||||
impl<K> MultiPartyDecryptor<bool, <Mat as Matrix>::R> for K
|
||||
where
|
||||
K: NonInteractiveMultiPartyClientKey,
|
||||
<Mat as Matrix>::R:
|
||||
TryConvertFrom1<[K::Element], CiphertextModulus<<Mat as Matrix>::MatElement>>,
|
||||
{
|
||||
type DecryptionShare = <Mat as Matrix>::MatElement;
|
||||
|
||||
fn gen_decryption_share(&self, c: &<Mat as Matrix>::R) -> Self::DecryptionShare {
|
||||
BoolEvaluator::with_local(|e| {
|
||||
DefaultSecureRng::with_local_mut(|rng| {
|
||||
multi_party_decryption_share(
|
||||
c,
|
||||
self.sk_rlwe().as_slice(),
|
||||
e.pbs_info().modop_rlweq(),
|
||||
rng,
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn aggregate_decryption_shares(
|
||||
&self,
|
||||
c: &<Mat as Matrix>::R,
|
||||
shares: &[Self::DecryptionShare],
|
||||
) -> bool {
|
||||
BoolEvaluator::with_local(|e| {
|
||||
let noisy_m = multi_party_aggregate_decryption_shares_and_decrypt(
|
||||
c,
|
||||
shares,
|
||||
e.pbs_info().modop_rlweq(),
|
||||
);
|
||||
|
||||
e.pbs_info().rlwe_q().decode(noisy_m)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl KeySwitchWithId<Mat> for Mat {
|
||||
/// Key switch RLWE ciphertext `Self` from user j's RLWE secret u_j
|
||||
/// to ideal RLWE secret `s` of non-interactive multi-party protocol.
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use itertools::izip;
|
||||
use num_traits::Zero;
|
||||
|
||||
use crate::{
|
||||
backend::{GetModulus, VectorOps},
|
||||
backend::{GetModulus, Modulus, VectorOps},
|
||||
ntt::Ntt,
|
||||
random::{RandomFillGaussianInModulus, RandomFillUniformInModulus},
|
||||
random::{
|
||||
RandomFillGaussianInModulus, RandomFillUniformInModulus, RandomGaussianElementInModulus,
|
||||
},
|
||||
utils::TryConvertFrom1,
|
||||
Matrix, MatrixEntity, MatrixMut, Row, RowEntity, RowMut,
|
||||
ArithmeticOps, Matrix, MatrixEntity, MatrixMut, Row, RowEntity, RowMut,
|
||||
};
|
||||
|
||||
pub(crate) fn public_key_share<
|
||||
@@ -50,6 +53,59 @@ pub(crate) fn public_key_share<
|
||||
modop.elwise_add_mut(share_out.as_mut(), s.as_ref()); // s*e + e
|
||||
}
|
||||
|
||||
/// Generate decryption share for LWE ciphertext `lwe_ct` with user's secret `s`
|
||||
pub(crate) fn multi_party_decryption_share<
|
||||
R: RowMut + RowEntity,
|
||||
Mod: Modulus<Element = R::Element>,
|
||||
ModOp: ArithmeticOps<Element = R::Element> + VectorOps<Element = R::Element> + GetModulus<M = Mod>,
|
||||
Rng: RandomGaussianElementInModulus<R::Element, Mod>,
|
||||
S,
|
||||
>(
|
||||
lwe_ct: &R,
|
||||
s: &[S],
|
||||
mod_op: &ModOp,
|
||||
rng: &mut Rng,
|
||||
) -> R::Element
|
||||
where
|
||||
R: TryConvertFrom1<[S], Mod>,
|
||||
R::Element: Zero,
|
||||
{
|
||||
assert!(lwe_ct.as_ref().len() == s.len() + 1);
|
||||
let mut neg_s = R::try_convert_from(s, mod_op.modulus());
|
||||
mod_op.elwise_neg_mut(neg_s.as_mut());
|
||||
|
||||
// share = (\sum -s_i * a_i) + e
|
||||
let mut share = R::Element::zero();
|
||||
izip!(neg_s.as_ref().iter(), lwe_ct.as_ref().iter().skip(1)).for_each(|(si, ai)| {
|
||||
share = mod_op.add(&share, &mod_op.mul(si, ai));
|
||||
});
|
||||
|
||||
let e = rng.random(mod_op.modulus());
|
||||
share = mod_op.add(&share, &e);
|
||||
|
||||
share
|
||||
}
|
||||
|
||||
/// Aggregate decryption shares for `lwe_ct` and return noisy decryption output
|
||||
/// `m + e`
|
||||
pub(crate) fn multi_party_aggregate_decryption_shares_and_decrypt<
|
||||
R: RowMut + RowEntity,
|
||||
ModOp: ArithmeticOps<Element = R::Element>,
|
||||
>(
|
||||
lwe_ct: &R,
|
||||
shares: &[R::Element],
|
||||
mod_op: &ModOp,
|
||||
) -> R::Element
|
||||
where
|
||||
R::Element: Zero,
|
||||
{
|
||||
let mut sum_shares = R::Element::zero();
|
||||
shares
|
||||
.iter()
|
||||
.for_each(|v| sum_shares = mod_op.add(&sum_shares, v));
|
||||
mod_op.add(&lwe_ct.as_ref()[0], &sum_shares)
|
||||
}
|
||||
|
||||
pub(crate) fn non_interactive_rgsw_ct<
|
||||
M: MatrixMut + MatrixEntity,
|
||||
S,
|
||||
|
||||
@@ -8,10 +8,11 @@ use crate::{
|
||||
RowMut, SampleExtractor,
|
||||
};
|
||||
|
||||
/// Fhe UInt8 type
|
||||
/// Fhe UInt8
|
||||
///
|
||||
/// - Stores encryptions of bits in little endian (i.e least signficant bit
|
||||
/// stored at 0th index and most signficant bit stores at 7th index)
|
||||
/// Note that `Self.data` stores encryptions of bits in little endian (i.e least
|
||||
/// signficant bit stored at 0th index and most signficant bit stores at 7th
|
||||
/// index)
|
||||
#[derive(Clone)]
|
||||
pub struct FheUint8<C> {
|
||||
pub(super) data: Vec<C>,
|
||||
@@ -27,7 +28,9 @@ impl<C> FheUint8<C> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Stored a batch of Fhe Uint8 ciphertext as collection of RLWE ciphertexts
|
||||
/// Stores a batch of Fhe Uint8 ciphertext as collection of unseeded RLWE
|
||||
/// ciphertexts always encrypted under the ideal RLWE secret `s` of the MPC
|
||||
/// protocol
|
||||
///
|
||||
/// To extract Fhe Uint8 ciphertext at `index` call `self.extract(index)`
|
||||
pub struct BatchedFheUint8<C> {
|
||||
@@ -37,6 +40,68 @@ pub struct BatchedFheUint8<C> {
|
||||
count: usize,
|
||||
}
|
||||
|
||||
impl<K, C> Encryptor<[u8], BatchedFheUint8<C>> for K
|
||||
where
|
||||
K: Encryptor<[bool], Vec<C>>,
|
||||
{
|
||||
/// Encrypt a batch of uint8s packed in vector of RLWE ciphertexts
|
||||
///
|
||||
/// Uint8s can be extracted from `BatchedFheUint8` with `SampleExtractor`
|
||||
fn encrypt(&self, m: &[u8]) -> BatchedFheUint8<C> {
|
||||
let bool_m = m
|
||||
.iter()
|
||||
.flat_map(|v| {
|
||||
(0..8)
|
||||
.into_iter()
|
||||
.map(|i| ((*v >> i) & 1) == 1)
|
||||
.collect_vec()
|
||||
})
|
||||
.collect_vec();
|
||||
let cts = K::encrypt(&self, &bool_m);
|
||||
BatchedFheUint8 {
|
||||
data: cts,
|
||||
count: m.len(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: MatrixEntity + MatrixMut<MatElement = u64>> From<&SeededBatchedFheUint8<M::R, [u8; 32]>>
|
||||
for BatchedFheUint8<M>
|
||||
where
|
||||
<M as Matrix>::R: RowMut,
|
||||
{
|
||||
/// Unseeds collection of seeded RLWE ciphertext in SeededBatchedFheUint8
|
||||
/// and returns as `Self`
|
||||
fn from(value: &SeededBatchedFheUint8<M::R, [u8; 32]>) -> Self {
|
||||
BoolEvaluator::with_local(|e| {
|
||||
let parameters = e.parameters();
|
||||
let ring_size = parameters.rlwe_n().0;
|
||||
let rlwe_q = parameters.rlwe_q();
|
||||
|
||||
let mut prng = DefaultSecureRng::new_seeded(value.seed);
|
||||
let rlwes = value
|
||||
.data
|
||||
.iter()
|
||||
.map(|partb| {
|
||||
let mut rlwe = M::zeros(2, ring_size);
|
||||
|
||||
// sample A
|
||||
RandomFillUniformInModulus::random_fill(&mut prng, rlwe_q, rlwe.get_row_mut(0));
|
||||
|
||||
// Copy over B
|
||||
rlwe.get_row_mut(1).copy_from_slice(partb.as_ref());
|
||||
|
||||
rlwe
|
||||
})
|
||||
.collect_vec();
|
||||
Self {
|
||||
data: rlwes,
|
||||
count: value.count,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, R> SampleExtractor<FheUint8<R>> for BatchedFheUint8<C>
|
||||
where
|
||||
C: SampleExtractor<R>,
|
||||
@@ -82,8 +147,28 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Stores a batch of FheUint8s packed in a collection unseeded RLWE ciphertexts
|
||||
///
|
||||
/// `Self` stores unseeded RLWE ciphertexts encrypted under user's RLWE secret
|
||||
/// `u_j` and is different from `BatchFheUint8` which stores collection of RLWE
|
||||
/// ciphertexts under ideal RLWE secret `s` of the (non-interactive/interactive)
|
||||
/// MPC protocol.
|
||||
///
|
||||
/// To extract FheUint8s from `Self`'s collection of RLWE ciphertexts, first
|
||||
/// switch `Self` to `BatchFheUint8` with `key_switch(user_id)` where `user_id`
|
||||
/// is user's id. This key switches collection of RLWE ciphertexts from
|
||||
/// user's RLWE secret `u_j` to ideal RLWE secret `s` of the MPC protocol. Then
|
||||
/// proceed to use `SampleExtract` on `BatchFheUint8` (for ex, call
|
||||
/// `extract_at(0)` to extract FheUint8 stored at index 0)
|
||||
pub struct NonInteractiveBatchedFheUint8<C> {
|
||||
/// Vector of RLWE ciphertexts `C`
|
||||
data: Vec<C>,
|
||||
/// Count of FheUint8s packed in vector of RLWE ciphertexts
|
||||
count: usize,
|
||||
}
|
||||
|
||||
impl<M: MatrixEntity + MatrixMut<MatElement = u64>> From<&SeededBatchedFheUint8<M::R, [u8; 32]>>
|
||||
for BatchedFheUint8<M>
|
||||
for NonInteractiveBatchedFheUint8<M>
|
||||
where
|
||||
<M as Matrix>::R: RowMut,
|
||||
{
|
||||
@@ -119,6 +204,27 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> KeySwitchWithId<BatchedFheUint8<C>> for NonInteractiveBatchedFheUint8<C>
|
||||
where
|
||||
C: KeySwitchWithId<C>,
|
||||
{
|
||||
/// Key switch `Self`'s collection of RLWE cihertexts encrypted under user's
|
||||
/// RLWE secret `u_j` to ideal RLWE secret `s` of the MPC protocol.
|
||||
///
|
||||
/// - user_id: user id of user `j`
|
||||
fn key_switch(&self, user_id: usize) -> BatchedFheUint8<C> {
|
||||
let data = self
|
||||
.data
|
||||
.iter()
|
||||
.map(|c| c.key_switch(user_id))
|
||||
.collect_vec();
|
||||
BatchedFheUint8 {
|
||||
data,
|
||||
count: self.count,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SeededBatchedFheUint8<C, S> {
|
||||
/// Vector of Seeded RLWE ciphertexts `C`.
|
||||
///
|
||||
@@ -131,21 +237,12 @@ pub struct SeededBatchedFheUint8<C, S> {
|
||||
count: usize,
|
||||
}
|
||||
|
||||
impl<C, S> SeededBatchedFheUint8<C, S> {
|
||||
pub fn unseed<M>(&self) -> BatchedFheUint8<M>
|
||||
where
|
||||
BatchedFheUint8<M>: for<'a> From<&'a SeededBatchedFheUint8<C, S>>,
|
||||
M: Matrix<R = C>,
|
||||
{
|
||||
BatchedFheUint8::from(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, C, S> Encryptor<[u8], SeededBatchedFheUint8<C, S>> for K
|
||||
where
|
||||
K: Encryptor<[bool], (Vec<C>, S)>,
|
||||
{
|
||||
/// Encrypt a slice of u8s of arbitray length as `SeededBatchedFheUint8`
|
||||
/// Encrypt a slice of u8s of arbitray length packed into collection of
|
||||
/// seeded RLWE ciphertexts and return `SeededBatchedFheUint8`
|
||||
fn encrypt(&self, m: &[u8]) -> SeededBatchedFheUint8<C, S> {
|
||||
// convert vector of u8s to vector bools
|
||||
let bool_m = m
|
||||
@@ -161,46 +258,42 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, C> Encryptor<[u8], BatchedFheUint8<C>> for K
|
||||
where
|
||||
K: Encryptor<[bool], Vec<C>>,
|
||||
{
|
||||
fn encrypt(&self, m: &[u8]) -> BatchedFheUint8<C> {
|
||||
let bool_m = m
|
||||
.iter()
|
||||
.flat_map(|v| {
|
||||
(0..8)
|
||||
.into_iter()
|
||||
.map(|i| ((*v >> i) & 1) == 1)
|
||||
.collect_vec()
|
||||
})
|
||||
.collect_vec();
|
||||
let cts = K::encrypt(&self, &bool_m);
|
||||
BatchedFheUint8 {
|
||||
data: cts,
|
||||
count: m.len(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> KeySwitchWithId<BatchedFheUint8<C>> for BatchedFheUint8<C>
|
||||
where
|
||||
C: KeySwitchWithId<C>,
|
||||
{
|
||||
/// Key switching collection of RLWE ciphertexts in `BatchedFheUint8` from
|
||||
/// user j's RLWE secret u_j to ideal RLWE secret key `s` of the protocol.
|
||||
impl<C, S> SeededBatchedFheUint8<C, S> {
|
||||
/// Unseed collection of seeded RLWE ciphertexts of `Self` and returns
|
||||
/// `NonInteractiveBatchedFheUint8` with collection of unseeded RLWE
|
||||
/// ciphertexts.
|
||||
///
|
||||
/// - user_id: user id of user j
|
||||
fn key_switch(&self, user_id: usize) -> BatchedFheUint8<C> {
|
||||
let data = self
|
||||
.data
|
||||
.iter()
|
||||
.map(|c| c.key_switch(user_id))
|
||||
.collect_vec();
|
||||
BatchedFheUint8 {
|
||||
data,
|
||||
count: self.count,
|
||||
}
|
||||
/// In non-interactive MPC setting, RLWE ciphertexts are encrypted under
|
||||
/// user's RLWE secret `u_j`. The RLWE ciphertexts must be key switched to
|
||||
/// ideal RLWE secret `s` of the MPC protocol before use.
|
||||
///
|
||||
/// Note that we don't provide `unseed` API from `Self` to
|
||||
/// `BatchedFheUint8`. This is because:
|
||||
///
|
||||
/// - In non-interactive setting (1) client encrypts private inputs using
|
||||
/// their secret `u_j` as `SeededBatchedFheUint8` and sends it to the
|
||||
/// server. (2) Server unseeds `SeededBatchedFheUint8` into
|
||||
/// `NonInteractiveBatchedFheUint8` indicating that private inputs are
|
||||
/// still encrypted under user's RLWE secret `u_j`. (3) Server key
|
||||
/// switches `NonInteractiveBatchedFheUint8` from user's RLWE secret `u_j`
|
||||
/// to ideal RLWE secret `s` and outputs `BatchedFheUint8`. (4)
|
||||
/// `BatchedFheUint8` always stores RLWE secret under ideal RLWE secret of
|
||||
/// the protocol. Hence, it is safe to extract FheUint8s. Server proceeds
|
||||
/// to extract necessary FheUint8s.
|
||||
///
|
||||
/// - In interactive setting (1) client always encrypts private inputs using
|
||||
/// public key corresponding to ideal RLWE secret `s` of the protocol and
|
||||
/// produces `BatchedFheUint8`. (2) Given `BatchedFheUint8` stores
|
||||
/// collection of RLWE ciphertext under ideal RLWE secret `s`, server can
|
||||
/// directly extract necessary FheUint8s to use.
|
||||
///
|
||||
/// Thus, there's no need to go directly from `Self` to `BatchedFheUint8`.
|
||||
pub fn unseed<M>(&self) -> NonInteractiveBatchedFheUint8<M>
|
||||
where
|
||||
NonInteractiveBatchedFheUint8<M>: for<'a> From<&'a SeededBatchedFheUint8<C, S>>,
|
||||
M: Matrix<R = C>,
|
||||
{
|
||||
NonInteractiveBatchedFheUint8::from(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user