mirror of
https://github.com/zama-ai/tfhe-rs.git
synced 2026-01-09 14:47:56 -05:00
refactor!: Use NonZero<T> in DataKind
Change the type used to store a block count in DataKind to NonZero. This makes it impossible to store 'empty' kinds such as DataKind::Unsigned(0), DataKind::Signed(0). Also, when deserializing, if the count is zero and error will be returned, adding an additional layer of sanitization.
This commit is contained in:
@@ -45,7 +45,7 @@ impl crate::FheTypes {
|
|||||||
Some(match data_kind {
|
Some(match data_kind {
|
||||||
DataKind::Unsigned(n) => {
|
DataKind::Unsigned(n) => {
|
||||||
let num_bits_per_block = message_modulus.0.ilog2() as usize;
|
let num_bits_per_block = message_modulus.0.ilog2() as usize;
|
||||||
let num_bits = n * num_bits_per_block;
|
let num_bits = n.get() * num_bits_per_block;
|
||||||
match num_bits {
|
match num_bits {
|
||||||
2 => Self::Uint2,
|
2 => Self::Uint2,
|
||||||
4 => Self::Uint4,
|
4 => Self::Uint4,
|
||||||
@@ -93,7 +93,7 @@ impl crate::FheTypes {
|
|||||||
}
|
}
|
||||||
DataKind::Signed(n) => {
|
DataKind::Signed(n) => {
|
||||||
let num_bits_per_block = message_modulus.0.ilog2() as usize;
|
let num_bits_per_block = message_modulus.0.ilog2() as usize;
|
||||||
let num_bits = n * num_bits_per_block;
|
let num_bits = n.get() * num_bits_per_block;
|
||||||
match num_bits {
|
match num_bits {
|
||||||
2 => Self::Int2,
|
2 => Self::Int2,
|
||||||
4 => Self::Int4,
|
4 => Self::Int4,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use std::num::NonZero;
|
||||||
use tfhe_versionable::{Unversionize, UnversionizeError, Versionize, VersionizeOwned};
|
use tfhe_versionable::{Unversionize, UnversionizeError, Versionize, VersionizeOwned};
|
||||||
|
|
||||||
use super::details::MaybeCloned;
|
use super::details::MaybeCloned;
|
||||||
@@ -40,14 +41,18 @@ impl<Id: FheUintId> HlCompressible for FheUint<Id> {
|
|||||||
match self.ciphertext {
|
match self.ciphertext {
|
||||||
crate::high_level_api::integers::unsigned::RadixCiphertext::Cpu(cpu_radix) => {
|
crate::high_level_api::integers::unsigned::RadixCiphertext::Cpu(cpu_radix) => {
|
||||||
let blocks = cpu_radix.blocks;
|
let blocks = cpu_radix.blocks;
|
||||||
let kind = DataKind::Unsigned(blocks.len());
|
if let Some(n) = NonZero::new(blocks.len()) {
|
||||||
messages.push((ToBeCompressed::Cpu(blocks), kind));
|
let kind = DataKind::Unsigned(n);
|
||||||
|
messages.push((ToBeCompressed::Cpu(blocks), kind));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
crate::high_level_api::integers::unsigned::RadixCiphertext::Cuda(gpu_radix) => {
|
crate::high_level_api::integers::unsigned::RadixCiphertext::Cuda(gpu_radix) => {
|
||||||
let blocks = gpu_radix.ciphertext;
|
let blocks = gpu_radix.ciphertext;
|
||||||
let kind = DataKind::Unsigned(blocks.info.blocks.len());
|
if let Some(n) = NonZero::new(blocks.info.blocks.len()) {
|
||||||
messages.push((ToBeCompressed::Cuda(blocks), kind));
|
let kind = DataKind::Unsigned(n);
|
||||||
|
messages.push((ToBeCompressed::Cuda(blocks), kind));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#[cfg(feature = "hpu")]
|
#[cfg(feature = "hpu")]
|
||||||
crate::high_level_api::integers::unsigned::RadixCiphertext::Hpu(_) => {
|
crate::high_level_api::integers::unsigned::RadixCiphertext::Hpu(_) => {
|
||||||
@@ -61,14 +66,18 @@ impl<Id: FheIntId> HlCompressible for FheInt<Id> {
|
|||||||
match self.ciphertext {
|
match self.ciphertext {
|
||||||
crate::high_level_api::integers::signed::SignedRadixCiphertext::Cpu(cpu_radix) => {
|
crate::high_level_api::integers::signed::SignedRadixCiphertext::Cpu(cpu_radix) => {
|
||||||
let blocks = cpu_radix.blocks;
|
let blocks = cpu_radix.blocks;
|
||||||
let kind = DataKind::Signed(blocks.len());
|
if let Some(n) = NonZero::new(blocks.len()) {
|
||||||
messages.push((ToBeCompressed::Cpu(blocks), kind));
|
let kind = DataKind::Signed(n);
|
||||||
|
messages.push((ToBeCompressed::Cpu(blocks), kind));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
crate::high_level_api::integers::signed::SignedRadixCiphertext::Cuda(gpu_radix) => {
|
crate::high_level_api::integers::signed::SignedRadixCiphertext::Cuda(gpu_radix) => {
|
||||||
let blocks = gpu_radix.ciphertext;
|
let blocks = gpu_radix.ciphertext;
|
||||||
let kind = DataKind::Signed(blocks.info.blocks.len());
|
if let Some(n) = NonZero::new(blocks.info.blocks.len()) {
|
||||||
messages.push((ToBeCompressed::Cuda(blocks), kind));
|
let kind = DataKind::Signed(n);
|
||||||
|
messages.push((ToBeCompressed::Cuda(blocks), kind));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -646,7 +655,7 @@ pub mod gpu {
|
|||||||
self,
|
self,
|
||||||
messages: &mut Vec<CudaRadixCiphertext>,
|
messages: &mut Vec<CudaRadixCiphertext>,
|
||||||
streams: &CudaStreams,
|
streams: &CudaStreams,
|
||||||
) -> DataKind {
|
) -> Option<DataKind> {
|
||||||
self.ciphertext
|
self.ciphertext
|
||||||
.into_gpu(streams)
|
.into_gpu(streams)
|
||||||
.compress_into(messages, streams)
|
.compress_into(messages, streams)
|
||||||
@@ -658,7 +667,7 @@ pub mod gpu {
|
|||||||
self,
|
self,
|
||||||
messages: &mut Vec<CudaRadixCiphertext>,
|
messages: &mut Vec<CudaRadixCiphertext>,
|
||||||
streams: &CudaStreams,
|
streams: &CudaStreams,
|
||||||
) -> DataKind {
|
) -> Option<DataKind> {
|
||||||
self.ciphertext
|
self.ciphertext
|
||||||
.into_gpu(streams)
|
.into_gpu(streams)
|
||||||
.compress_into(messages, streams)
|
.compress_into(messages, streams)
|
||||||
@@ -670,7 +679,7 @@ pub mod gpu {
|
|||||||
self,
|
self,
|
||||||
messages: &mut Vec<CudaRadixCiphertext>,
|
messages: &mut Vec<CudaRadixCiphertext>,
|
||||||
streams: &CudaStreams,
|
streams: &CudaStreams,
|
||||||
) -> DataKind {
|
) -> Option<DataKind> {
|
||||||
self.ciphertext
|
self.ciphertext
|
||||||
.into_gpu(streams)
|
.into_gpu(streams)
|
||||||
.compress_into(messages, streams)
|
.compress_into(messages, streams)
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ use crate::named::Named;
|
|||||||
use crate::shortint::ciphertext::SquashedNoiseCiphertext;
|
use crate::shortint::ciphertext::SquashedNoiseCiphertext;
|
||||||
use crate::{SquashedNoiseFheBool, SquashedNoiseFheInt, SquashedNoiseFheUint, Tag, Versionize};
|
use crate::{SquashedNoiseFheBool, SquashedNoiseFheInt, SquashedNoiseFheUint, Tag, Versionize};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::num::NonZero;
|
||||||
use tfhe_versionable::{Unversionize, UnversionizeError, VersionizeOwned};
|
use tfhe_versionable::{Unversionize, UnversionizeError, VersionizeOwned};
|
||||||
|
|
||||||
pub(in crate::high_level_api) enum InnerCompressedSquashedNoiseCiphertextList {
|
pub(in crate::high_level_api) enum InnerCompressedSquashedNoiseCiphertextList {
|
||||||
@@ -223,7 +224,10 @@ impl SquashedNoiseExpandable for SquashedNoiseFheUint {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(create_error_message(DataKind::Unsigned(0), kind))
|
Err(create_error_message(
|
||||||
|
DataKind::Unsigned(NonZero::new(1).unwrap()),
|
||||||
|
kind,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -242,7 +246,10 @@ impl SquashedNoiseExpandable for SquashedNoiseFheInt {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(create_error_message(DataKind::Signed(0), kind))
|
Err(create_error_message(
|
||||||
|
DataKind::Signed(NonZero::new(1).unwrap()),
|
||||||
|
kind,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -279,11 +286,14 @@ impl HlSquashedNoiseCompressible for SquashedNoiseFheUint {
|
|||||||
fn compress_into(self, messages: &mut Vec<(private::SquashedNoiseToBeCompressed, DataKind)>) {
|
fn compress_into(self, messages: &mut Vec<(private::SquashedNoiseToBeCompressed, DataKind)>) {
|
||||||
match self.inner {
|
match self.inner {
|
||||||
InnerSquashedNoiseRadixCiphertext::Cpu(cpu_ct) => {
|
InnerSquashedNoiseRadixCiphertext::Cpu(cpu_ct) => {
|
||||||
let kind = DataKind::Unsigned(cpu_ct.original_block_count);
|
if cpu_ct.original_block_count != 0 {
|
||||||
messages.push((
|
let kind =
|
||||||
private::SquashedNoiseToBeCompressed::Cpu(cpu_ct.packed_blocks),
|
DataKind::Unsigned(NonZero::new(cpu_ct.original_block_count).unwrap());
|
||||||
kind,
|
messages.push((
|
||||||
))
|
private::SquashedNoiseToBeCompressed::Cpu(cpu_ct.packed_blocks),
|
||||||
|
kind,
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -293,11 +303,13 @@ impl HlSquashedNoiseCompressible for SquashedNoiseFheInt {
|
|||||||
fn compress_into(self, messages: &mut Vec<(private::SquashedNoiseToBeCompressed, DataKind)>) {
|
fn compress_into(self, messages: &mut Vec<(private::SquashedNoiseToBeCompressed, DataKind)>) {
|
||||||
match self.inner {
|
match self.inner {
|
||||||
InnerSquashedNoiseSignedRadixCiphertext::Cpu(cpu_ct) => {
|
InnerSquashedNoiseSignedRadixCiphertext::Cpu(cpu_ct) => {
|
||||||
let kind = DataKind::Signed(cpu_ct.original_block_count);
|
if cpu_ct.original_block_count() != 0 {
|
||||||
messages.push((
|
let kind = DataKind::Signed(NonZero::new(cpu_ct.original_block_count).unwrap());
|
||||||
private::SquashedNoiseToBeCompressed::Cpu(cpu_ct.packed_blocks),
|
messages.push((
|
||||||
kind,
|
private::SquashedNoiseToBeCompressed::Cpu(cpu_ct.packed_blocks),
|
||||||
))
|
kind,
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -432,10 +432,14 @@ impl crate::HlCompressible for FheAsciiString {
|
|||||||
AsciiDevice::Cpu(fhe_string) => {
|
AsciiDevice::Cpu(fhe_string) => {
|
||||||
let mut blocks = vec![];
|
let mut blocks = vec![];
|
||||||
let data_kind = fhe_string.compress_into(&mut blocks);
|
let data_kind = fhe_string.compress_into(&mut blocks);
|
||||||
messages.push((
|
if let Some(data_kind) = data_kind {
|
||||||
crate::high_level_api::compressed_ciphertext_list::ToBeCompressed::Cpu(blocks),
|
messages.push((
|
||||||
data_kind,
|
crate::high_level_api::compressed_ciphertext_list::ToBeCompressed::Cpu(
|
||||||
));
|
blocks,
|
||||||
|
),
|
||||||
|
data_kind,
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
use std::convert::Infallible;
|
|
||||||
|
|
||||||
use tfhe_versionable::{Upgrade, Version, VersionsDispatch};
|
|
||||||
|
|
||||||
use crate::integer::ciphertext::{
|
use crate::integer::ciphertext::{
|
||||||
BaseCrtCiphertext, BaseRadixCiphertext, BaseSignedRadixCiphertext, CompactCiphertextList,
|
BaseCrtCiphertext, BaseRadixCiphertext, BaseSignedRadixCiphertext, CompactCiphertextList,
|
||||||
CompressedCiphertextList, CompressedModulusSwitchedRadixCiphertext,
|
CompressedCiphertextList, CompressedModulusSwitchedRadixCiphertext,
|
||||||
@@ -13,6 +9,9 @@ use crate::integer::BooleanBlock;
|
|||||||
#[cfg(feature = "zk-pok")]
|
#[cfg(feature = "zk-pok")]
|
||||||
use crate::integer::ProvenCompactCiphertextList;
|
use crate::integer::ProvenCompactCiphertextList;
|
||||||
use crate::shortint::ciphertext::CompressedModulusSwitchedCiphertext;
|
use crate::shortint::ciphertext::CompressedModulusSwitchedCiphertext;
|
||||||
|
use std::convert::Infallible;
|
||||||
|
use std::num::NonZero;
|
||||||
|
use tfhe_versionable::{Upgrade, Version, VersionsDispatch};
|
||||||
|
|
||||||
#[derive(VersionsDispatch)]
|
#[derive(VersionsDispatch)]
|
||||||
pub enum BaseRadixCiphertextVersions<Block> {
|
pub enum BaseRadixCiphertextVersions<Block> {
|
||||||
@@ -44,7 +43,10 @@ impl Upgrade<CompactCiphertextList> for CompactCiphertextListV0 {
|
|||||||
// Since we can't guess the type of data here, we set them by default as unsigned integer.
|
// Since we can't guess the type of data here, we set them by default as unsigned integer.
|
||||||
// Since it this data comes from 0.6, if it is included in a homogeneous compact list it
|
// Since it this data comes from 0.6, if it is included in a homogeneous compact list it
|
||||||
// will be converted to the right type at expand time.
|
// will be converted to the right type at expand time.
|
||||||
let info = vec![DataKind::Unsigned(self.num_blocks_per_integer); radix_count];
|
|
||||||
|
let info = NonZero::new(self.num_blocks_per_integer)
|
||||||
|
.map(|n| vec![DataKind::Unsigned(n); radix_count])
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
Ok(CompactCiphertextList::from_raw_parts(self.ct_list, info))
|
Ok(CompactCiphertextList::from_raw_parts(self.ct_list, info))
|
||||||
}
|
}
|
||||||
@@ -62,9 +64,40 @@ pub enum ProvenCompactCiphertextListVersions {
|
|||||||
V0(ProvenCompactCiphertextList),
|
V0(ProvenCompactCiphertextList),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Version)]
|
||||||
|
pub enum DataKindV0 {
|
||||||
|
/// The held value is a number of radix blocks.
|
||||||
|
Unsigned(usize),
|
||||||
|
/// The held value is a number of radix blocks.
|
||||||
|
Signed(usize),
|
||||||
|
Boolean,
|
||||||
|
String {
|
||||||
|
n_chars: u32,
|
||||||
|
padded: bool,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(VersionsDispatch)]
|
#[derive(VersionsDispatch)]
|
||||||
pub enum DataKindVersions {
|
pub enum DataKindVersions {
|
||||||
V0(DataKind),
|
V0(DataKindV0),
|
||||||
|
V1(DataKind),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Upgrade<DataKind> for DataKindV0 {
|
||||||
|
type Error = crate::Error;
|
||||||
|
|
||||||
|
fn upgrade(self) -> Result<DataKind, Self::Error> {
|
||||||
|
match self {
|
||||||
|
Self::Unsigned(n) => NonZero::new(n)
|
||||||
|
.ok_or_else(|| crate::error!("DataKind::Unsigned requires non-zero block count"))
|
||||||
|
.map(DataKind::Unsigned),
|
||||||
|
Self::Signed(n) => NonZero::new(n)
|
||||||
|
.ok_or_else(|| crate::error!("DataKind::Signed requires non-zero block count"))
|
||||||
|
.map(DataKind::Signed),
|
||||||
|
Self::Boolean => Ok(DataKind::Boolean),
|
||||||
|
Self::String { n_chars, padded } => Ok(DataKind::String { n_chars, padded }),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(VersionsDispatch)]
|
#[derive(VersionsDispatch)]
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ use crate::zk::{
|
|||||||
CompactPkeCrs, CompactPkeProofConformanceParams, ZkComputeLoad, ZkPkeV2HashMode,
|
CompactPkeCrs, CompactPkeProofConformanceParams, ZkComputeLoad, ZkPkeV2HashMode,
|
||||||
ZkVerificationOutcome,
|
ZkVerificationOutcome,
|
||||||
};
|
};
|
||||||
|
use std::num::NonZero;
|
||||||
|
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -110,7 +111,7 @@ pub trait Compactable {
|
|||||||
messages: &mut Vec<u64>,
|
messages: &mut Vec<u64>,
|
||||||
message_modulus: MessageModulus,
|
message_modulus: MessageModulus,
|
||||||
num_blocks: Option<usize>,
|
num_blocks: Option<usize>,
|
||||||
) -> DataKind;
|
) -> Option<DataKind>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Compactable for bool {
|
impl Compactable for bool {
|
||||||
@@ -119,9 +120,9 @@ impl Compactable for bool {
|
|||||||
messages: &mut Vec<u64>,
|
messages: &mut Vec<u64>,
|
||||||
_message_modulus: MessageModulus,
|
_message_modulus: MessageModulus,
|
||||||
_num_blocks: Option<usize>,
|
_num_blocks: Option<usize>,
|
||||||
) -> DataKind {
|
) -> Option<DataKind> {
|
||||||
messages.push(self as u64);
|
messages.push(self as u64);
|
||||||
DataKind::Boolean
|
Some(DataKind::Boolean)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,18 +135,19 @@ where
|
|||||||
messages: &mut Vec<u64>,
|
messages: &mut Vec<u64>,
|
||||||
message_modulus: MessageModulus,
|
message_modulus: MessageModulus,
|
||||||
num_blocks: Option<usize>,
|
num_blocks: Option<usize>,
|
||||||
) -> DataKind {
|
) -> Option<DataKind> {
|
||||||
let num_blocks =
|
let num_blocks =
|
||||||
num_blocks.unwrap_or_else(|| T::BITS.div_ceil(message_modulus.0.ilog2() as usize));
|
num_blocks.unwrap_or_else(|| T::BITS.div_ceil(message_modulus.0.ilog2() as usize));
|
||||||
let decomposer = create_clear_radix_block_iterator(self, message_modulus, num_blocks);
|
let num_blocks = NonZero::new(num_blocks)?;
|
||||||
|
let decomposer = create_clear_radix_block_iterator(self, message_modulus, num_blocks.get());
|
||||||
messages.extend(decomposer);
|
messages.extend(decomposer);
|
||||||
|
|
||||||
// This works because rust always uses two's complement
|
// This works because rust always uses two's complement
|
||||||
let is_signed = (T::ONE << (T::BITS - 1)) < T::ZERO;
|
let is_signed = (T::ONE << (T::BITS - 1)) < T::ZERO;
|
||||||
if is_signed {
|
if is_signed {
|
||||||
DataKind::Signed(num_blocks)
|
Some(DataKind::Signed(num_blocks))
|
||||||
} else {
|
} else {
|
||||||
DataKind::Unsigned(num_blocks)
|
Some(DataKind::Unsigned(num_blocks))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -165,18 +167,44 @@ impl CompactCiphertextListBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Pushes Some(kind), and checks that the current block count
|
||||||
|
/// is coherent with the pushed kind
|
||||||
|
///
|
||||||
|
/// This is to be called after `Compactable::compact_into`
|
||||||
|
///
|
||||||
|
/// `count_before` block count before calling Compactable::compact_into
|
||||||
|
/// `maybe_kind`: the kind returned by the Compactable::compact_into call
|
||||||
|
fn push_and_check_kind_coherence(
|
||||||
|
&mut self,
|
||||||
|
count_before: usize,
|
||||||
|
maybe_kind: Option<DataKind>,
|
||||||
|
) -> Result<(), ()> {
|
||||||
|
let added_blocks = match maybe_kind {
|
||||||
|
Some(kind) => {
|
||||||
|
let msg_modulus = self.pk.key.message_modulus();
|
||||||
|
self.info.push(kind);
|
||||||
|
kind.num_blocks(msg_modulus)
|
||||||
|
}
|
||||||
|
None => 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.messages.len() == count_before + added_blocks {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn push<T>(&mut self, data: T) -> &mut Self
|
pub fn push<T>(&mut self, data: T) -> &mut Self
|
||||||
where
|
where
|
||||||
T: Compactable,
|
T: Compactable,
|
||||||
{
|
{
|
||||||
let n = self.messages.len();
|
let n = self.messages.len();
|
||||||
let msg_modulus = self.pk.key.message_modulus();
|
let msg_modulus = self.pk.key.message_modulus();
|
||||||
let kind = data.compact_into(&mut self.messages, msg_modulus, None);
|
let maybe_kind = data.compact_into(&mut self.messages, msg_modulus, None);
|
||||||
assert_eq!(n + kind.num_blocks(msg_modulus), self.messages.len());
|
|
||||||
|
|
||||||
if kind.num_blocks(msg_modulus) != 0 {
|
self.push_and_check_kind_coherence(n, maybe_kind)
|
||||||
self.info.push(kind);
|
.expect("Internal error: non coherent block count after push");
|
||||||
}
|
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -191,10 +219,11 @@ impl CompactCiphertextListBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let n = self.messages.len();
|
let n = self.messages.len();
|
||||||
let msg_mod = self.pk.key.message_modulus();
|
let msg_modulus = self.pk.key.message_modulus();
|
||||||
let kind = data.compact_into(&mut self.messages, msg_mod, Some(num_blocks));
|
let maybe_kind = data.compact_into(&mut self.messages, msg_modulus, Some(num_blocks));
|
||||||
assert_eq!(n + kind.num_blocks(msg_mod), self.messages.len());
|
|
||||||
self.info.push(kind);
|
self.push_and_check_kind_coherence(n, maybe_kind)
|
||||||
|
.expect("Internal error: non coherent block count after push");
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -741,12 +770,7 @@ fn expansion_helper<ListType>(
|
|||||||
|
|
||||||
impl CompactCiphertextList {
|
impl CompactCiphertextList {
|
||||||
pub fn is_packed(&self) -> bool {
|
pub fn is_packed(&self) -> bool {
|
||||||
self.ct_list.degree.get()
|
self.ct_list.is_packed()
|
||||||
> self
|
|
||||||
.ct_list
|
|
||||||
.message_modulus
|
|
||||||
.corresponding_max_degree()
|
|
||||||
.get()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn needs_casting(&self) -> bool {
|
pub fn needs_casting(&self) -> bool {
|
||||||
@@ -853,7 +877,7 @@ impl CompactCiphertextList {
|
|||||||
/// assert_eq!(-1i8, sanity_decrypted);
|
/// assert_eq!(-1i8, sanity_decrypted);
|
||||||
///
|
///
|
||||||
/// compact_ct
|
/// compact_ct
|
||||||
/// .reinterpret_data(&[DataKind::Unsigned(num_blocks)])
|
/// .reinterpret_data(&[DataKind::Unsigned(num_blocks.try_into().unwrap())])
|
||||||
/// .unwrap();
|
/// .unwrap();
|
||||||
///
|
///
|
||||||
/// let expander = compact_ct
|
/// let expander = compact_ct
|
||||||
@@ -892,7 +916,7 @@ impl CompactCiphertextList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn ciphertext_count(&self) -> usize {
|
pub fn ciphertext_count(&self) -> usize {
|
||||||
self.info.len()
|
self.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expand(
|
pub fn expand(
|
||||||
@@ -983,6 +1007,13 @@ impl ProvenCompactCiphertextList {
|
|||||||
metadata: &[u8],
|
metadata: &[u8],
|
||||||
expansion_mode: IntegerCompactCiphertextListExpansionMode<'_>,
|
expansion_mode: IntegerCompactCiphertextListExpansionMode<'_>,
|
||||||
) -> crate::Result<CompactCiphertextListExpander> {
|
) -> crate::Result<CompactCiphertextListExpander> {
|
||||||
|
if self.is_empty() {
|
||||||
|
if self.verify(crs, public_key, metadata) == ZkVerificationOutcome::Invalid {
|
||||||
|
return Err(crate::ErrorKind::InvalidZkProof.into());
|
||||||
|
}
|
||||||
|
return Ok(CompactCiphertextListExpander::new(vec![], vec![]));
|
||||||
|
}
|
||||||
|
|
||||||
let is_packed = self.is_packed();
|
let is_packed = self.is_packed();
|
||||||
|
|
||||||
// Type annotation needed rust is not able to coerce the type on its own, also forces us to
|
// Type annotation needed rust is not able to coerce the type on its own, also forces us to
|
||||||
@@ -1038,12 +1069,11 @@ impl ProvenCompactCiphertextList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_packed(&self) -> bool {
|
pub fn is_packed(&self) -> bool {
|
||||||
self.ct_list.proved_lists[0].0.degree.get()
|
if self.is_empty() {
|
||||||
> self.ct_list.proved_lists[0]
|
return false;
|
||||||
.0
|
}
|
||||||
.message_modulus
|
|
||||||
.corresponding_max_degree()
|
self.ct_list.proved_lists[0].0.is_packed()
|
||||||
.get()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn needs_casting(&self) -> bool {
|
pub fn needs_casting(&self) -> bool {
|
||||||
@@ -1136,11 +1166,25 @@ impl ParameterSetConformant for ProvenCompactCiphertextList {
|
|||||||
fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool {
|
fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool {
|
||||||
let Self { ct_list, info } = self;
|
let Self { ct_list, info } = self;
|
||||||
|
|
||||||
|
let is_packed = self.is_packed();
|
||||||
|
|
||||||
|
let all_have_same_packing = ct_list
|
||||||
|
.proved_lists
|
||||||
|
.iter()
|
||||||
|
.all(|(list, _)| list.is_packed() == is_packed);
|
||||||
|
|
||||||
|
if !all_have_same_packing {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
let total_expected_num_blocks: usize = info
|
let total_expected_num_blocks: usize = info
|
||||||
.iter()
|
.iter()
|
||||||
.map(|a| a.num_blocks(self.message_modulus()))
|
.map(|a| a.num_blocks(self.message_modulus()))
|
||||||
.sum();
|
.sum();
|
||||||
|
|
||||||
|
let total_expected_lwe_count =
|
||||||
|
total_expected_num_blocks.div_ceil(if is_packed { 2 } else { 1 });
|
||||||
|
|
||||||
let a = ProvenCompactCiphertextListConformanceParams {
|
let a = ProvenCompactCiphertextListConformanceParams {
|
||||||
expansion_kind: parameter_set.expansion_kind,
|
expansion_kind: parameter_set.expansion_kind,
|
||||||
encryption_lwe_dimension: parameter_set.encryption_lwe_dimension,
|
encryption_lwe_dimension: parameter_set.encryption_lwe_dimension,
|
||||||
@@ -1149,7 +1193,7 @@ impl ParameterSetConformant for ProvenCompactCiphertextList {
|
|||||||
ciphertext_modulus: parameter_set.ciphertext_modulus,
|
ciphertext_modulus: parameter_set.ciphertext_modulus,
|
||||||
max_lwe_count_per_compact_list: parameter_set.max_elements_per_compact_list,
|
max_lwe_count_per_compact_list: parameter_set.max_elements_per_compact_list,
|
||||||
// packing by 2
|
// packing by 2
|
||||||
total_expected_lwe_count: total_expected_num_blocks.div_ceil(2),
|
total_expected_lwe_count,
|
||||||
zk_conformance_params: parameter_set.zk_conformance_params,
|
zk_conformance_params: parameter_set.zk_conformance_params,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1159,7 +1203,7 @@ impl ParameterSetConformant for ProvenCompactCiphertextList {
|
|||||||
|
|
||||||
#[cfg(feature = "zk-pok")]
|
#[cfg(feature = "zk-pok")]
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod zk_pok_tests {
|
||||||
// Test utils for tests here
|
// Test utils for tests here
|
||||||
impl ProvenCompactCiphertextList {
|
impl ProvenCompactCiphertextList {
|
||||||
/// For testing and creating potentially invalid lists
|
/// For testing and creating potentially invalid lists
|
||||||
@@ -1169,13 +1213,17 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
use super::{DataKind, ProvenCompactCiphertextList};
|
use super::{DataKind, ProvenCompactCiphertextList};
|
||||||
|
use crate::conformance::ParameterSetConformant;
|
||||||
use crate::core_crypto::prelude::LweCiphertextCount;
|
use crate::core_crypto::prelude::LweCiphertextCount;
|
||||||
use crate::integer::ciphertext::CompactCiphertextList;
|
use crate::integer::ciphertext::{
|
||||||
|
CompactCiphertextList, IntegerProvenCompactCiphertextListConformanceParams,
|
||||||
|
};
|
||||||
use crate::integer::key_switching_key::KeySwitchingKey;
|
use crate::integer::key_switching_key::KeySwitchingKey;
|
||||||
use crate::integer::parameters::IntegerCompactCiphertextListExpansionMode;
|
use crate::integer::parameters::IntegerCompactCiphertextListExpansionMode;
|
||||||
use crate::integer::{
|
use crate::integer::{
|
||||||
BooleanBlock, ClientKey, CompactPrivateKey, CompactPublicKey, RadixCiphertext, ServerKey,
|
BooleanBlock, ClientKey, CompactPrivateKey, CompactPublicKey, RadixCiphertext, ServerKey,
|
||||||
};
|
};
|
||||||
|
use crate::shortint::ciphertext::Degree;
|
||||||
use crate::shortint::parameters::test_params::{
|
use crate::shortint::parameters::test_params::{
|
||||||
TEST_PARAM_KEYSWITCH_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128_ZKV1,
|
TEST_PARAM_KEYSWITCH_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128_ZKV1,
|
||||||
TEST_PARAM_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128_ZKV1,
|
TEST_PARAM_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128_ZKV1,
|
||||||
@@ -1185,7 +1233,7 @@ mod tests {
|
|||||||
PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
|
PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
|
||||||
PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
|
PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
|
||||||
};
|
};
|
||||||
use crate::zk::{CompactPkeCrs, ZkComputeLoad};
|
use crate::zk::{CompactPkeCrs, ZkComputeLoad, ZkVerificationOutcome};
|
||||||
use rand::random;
|
use rand::random;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1250,6 +1298,240 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_empty_list() {
|
||||||
|
let pke_params = PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
|
||||||
|
let ksk_params = PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
|
||||||
|
let fhe_params = PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
|
||||||
|
|
||||||
|
let metadata = [b'i', b'n', b't', b'e', b'g', b'e', b'r'];
|
||||||
|
|
||||||
|
let crs = CompactPkeCrs::from_shortint_params(pke_params, LweCiphertextCount(512)).unwrap();
|
||||||
|
let cks = ClientKey::new(fhe_params);
|
||||||
|
let sk = ServerKey::new_radix_server_key(&cks);
|
||||||
|
let compact_private_key = CompactPrivateKey::new(pke_params);
|
||||||
|
let ksk = KeySwitchingKey::new((&compact_private_key, None), (&cks, &sk), ksk_params);
|
||||||
|
let pk = CompactPublicKey::new(&compact_private_key);
|
||||||
|
|
||||||
|
// Test by pushing with zero blocks
|
||||||
|
{
|
||||||
|
let proven_ct = CompactCiphertextList::builder(&pk)
|
||||||
|
.push_with_num_blocks(1u8, 0)
|
||||||
|
.push_with_num_blocks(-1i8, 0)
|
||||||
|
.build_with_proof_packed(&crs, &metadata, ZkComputeLoad::Proof)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert!(proven_ct.is_empty());
|
||||||
|
assert_eq!(proven_ct.len(), 0);
|
||||||
|
assert_eq!(
|
||||||
|
proven_ct.verify(&crs, &pk, &metadata),
|
||||||
|
ZkVerificationOutcome::Valid
|
||||||
|
);
|
||||||
|
assert!(matches!(
|
||||||
|
proven_ct.verify_and_expand(
|
||||||
|
&crs,
|
||||||
|
&pk,
|
||||||
|
&metadata,
|
||||||
|
IntegerCompactCiphertextListExpansionMode::CastAndUnpackIfNecessary(ksk.as_view()),
|
||||||
|
),
|
||||||
|
Ok(vec) if vec.is_empty()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test by pushing with nothing
|
||||||
|
{
|
||||||
|
let proven_ct = CompactCiphertextList::builder(&pk)
|
||||||
|
.build_with_proof_packed(&crs, &metadata, ZkComputeLoad::Proof)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert!(proven_ct.is_empty());
|
||||||
|
assert_eq!(proven_ct.len(), 0);
|
||||||
|
assert_eq!(
|
||||||
|
proven_ct.verify(&crs, &pk, &metadata),
|
||||||
|
ZkVerificationOutcome::Valid
|
||||||
|
);
|
||||||
|
assert!(matches!(
|
||||||
|
proven_ct.verify_and_expand(
|
||||||
|
&crs,
|
||||||
|
&pk,
|
||||||
|
&metadata,
|
||||||
|
IntegerCompactCiphertextListExpansionMode::CastAndUnpackIfNecessary(ksk.as_view()),
|
||||||
|
),
|
||||||
|
Ok(vec) if vec.is_empty()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// In this test we check the behavior of the proven list when the info vec
|
||||||
|
/// is modified
|
||||||
|
#[test]
|
||||||
|
fn test_attack_list_info() {
|
||||||
|
let pke_params = PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
|
||||||
|
let ksk_params = PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
|
||||||
|
let fhe_params = PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
|
||||||
|
|
||||||
|
let metadata = [b'i', b'n', b't', b'e', b'g', b'e', b'r'];
|
||||||
|
|
||||||
|
let crs = CompactPkeCrs::from_shortint_params(pke_params, LweCiphertextCount(2)).unwrap();
|
||||||
|
let cks = ClientKey::new(fhe_params);
|
||||||
|
let sk = ServerKey::new_radix_server_key(&cks);
|
||||||
|
let compact_private_key = CompactPrivateKey::new(pke_params);
|
||||||
|
let ksk = KeySwitchingKey::new((&compact_private_key, None), (&cks, &sk), ksk_params);
|
||||||
|
let pk = CompactPublicKey::new(&compact_private_key);
|
||||||
|
|
||||||
|
let conformance_params =
|
||||||
|
IntegerProvenCompactCiphertextListConformanceParams::from_crs_and_parameters(
|
||||||
|
pke_params, &crs,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut proven_ct = CompactCiphertextList::builder(&pk)
|
||||||
|
.push_with_num_blocks(1u8, 4)
|
||||||
|
.push_with_num_blocks(-1i8, 4)
|
||||||
|
.build_with_proof_packed(&crs, &metadata, ZkComputeLoad::Proof)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(proven_ct.len(), 2);
|
||||||
|
assert!(!proven_ct.is_empty());
|
||||||
|
assert_eq!(
|
||||||
|
proven_ct.info,
|
||||||
|
vec![
|
||||||
|
DataKind::Unsigned(4.try_into().unwrap()),
|
||||||
|
DataKind::Signed(4.try_into().unwrap())
|
||||||
|
]
|
||||||
|
);
|
||||||
|
assert_eq!(proven_ct.ct_list.proved_lists.len(), 2);
|
||||||
|
assert!(proven_ct.is_conformant(&conformance_params));
|
||||||
|
|
||||||
|
// Change the info vec, conformance should no longer work
|
||||||
|
let saved_info = std::mem::take(&mut proven_ct.info);
|
||||||
|
assert!(!proven_ct.is_conformant(&conformance_params));
|
||||||
|
assert!(proven_ct.is_empty());
|
||||||
|
assert_eq!(
|
||||||
|
proven_ct.verify(&crs, &pk, &metadata),
|
||||||
|
ZkVerificationOutcome::Valid
|
||||||
|
);
|
||||||
|
assert!(matches!(
|
||||||
|
proven_ct.verify_and_expand(
|
||||||
|
&crs,
|
||||||
|
&pk,
|
||||||
|
&metadata,
|
||||||
|
IntegerCompactCiphertextListExpansionMode::CastAndUnpackIfNecessary(ksk.
|
||||||
|
as_view()),
|
||||||
|
),
|
||||||
|
Ok(vec) if vec.is_empty()
|
||||||
|
));
|
||||||
|
|
||||||
|
// The info vec will still not be coherent (block number wise)
|
||||||
|
// so conformance fails, we still test verify_and_expand to know its
|
||||||
|
// behavior
|
||||||
|
proven_ct.info = vec![DataKind::Signed(4.try_into().unwrap())];
|
||||||
|
assert!(!proven_ct.is_conformant(&conformance_params));
|
||||||
|
assert!(!proven_ct.is_empty());
|
||||||
|
assert!(proven_ct.is_packed());
|
||||||
|
assert_eq!(
|
||||||
|
proven_ct.verify(&crs, &pk, &metadata),
|
||||||
|
ZkVerificationOutcome::Valid
|
||||||
|
);
|
||||||
|
assert!(proven_ct
|
||||||
|
.verify_and_expand(
|
||||||
|
&crs,
|
||||||
|
&pk,
|
||||||
|
&metadata,
|
||||||
|
IntegerCompactCiphertextListExpansionMode::CastAndUnpackIfNecessary(ksk.as_view())
|
||||||
|
)
|
||||||
|
.is_err());
|
||||||
|
|
||||||
|
// The info vec will be coherent (block number wise)
|
||||||
|
// so conformance passes. However, the info metadata is different from
|
||||||
|
// what it was originally
|
||||||
|
proven_ct
|
||||||
|
.info
|
||||||
|
.push(DataKind::Unsigned(4.try_into().unwrap()));
|
||||||
|
assert_ne!(proven_ct.info, saved_info);
|
||||||
|
assert!(proven_ct.is_conformant(&conformance_params));
|
||||||
|
assert!(!proven_ct.is_empty());
|
||||||
|
assert!(proven_ct.is_packed());
|
||||||
|
assert_eq!(
|
||||||
|
proven_ct.verify(&crs, &pk, &metadata),
|
||||||
|
ZkVerificationOutcome::Valid
|
||||||
|
);
|
||||||
|
assert!(proven_ct
|
||||||
|
.verify_and_expand(
|
||||||
|
&crs,
|
||||||
|
&pk,
|
||||||
|
&metadata,
|
||||||
|
IntegerCompactCiphertextListExpansionMode::CastAndUnpackIfNecessary(ksk.as_view())
|
||||||
|
)
|
||||||
|
.is_ok());
|
||||||
|
|
||||||
|
// The info vec now has more entry than there are blocks
|
||||||
|
// so conformance fails.
|
||||||
|
proven_ct.info.push(DataKind::Boolean);
|
||||||
|
assert!(!proven_ct.is_conformant(&conformance_params));
|
||||||
|
assert!(!proven_ct.is_empty());
|
||||||
|
assert!(proven_ct.is_packed());
|
||||||
|
assert_eq!(
|
||||||
|
proven_ct.verify(&crs, &pk, &metadata),
|
||||||
|
ZkVerificationOutcome::Valid
|
||||||
|
);
|
||||||
|
assert!(proven_ct
|
||||||
|
.verify_and_expand(
|
||||||
|
&crs,
|
||||||
|
&pk,
|
||||||
|
&metadata,
|
||||||
|
IntegerCompactCiphertextListExpansionMode::CastAndUnpackIfNecessary(ksk.as_view())
|
||||||
|
)
|
||||||
|
.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_attack_proven_list_metadata() {
|
||||||
|
let pke_params = PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
|
||||||
|
let ksk_params = PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
|
||||||
|
let fhe_params = PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
|
||||||
|
|
||||||
|
let metadata = [b'i', b'n', b't', b'e', b'g', b'e', b'r'];
|
||||||
|
|
||||||
|
let crs = CompactPkeCrs::from_shortint_params(pke_params, LweCiphertextCount(2)).unwrap();
|
||||||
|
let cks = ClientKey::new(fhe_params);
|
||||||
|
let sk = ServerKey::new_radix_server_key(&cks);
|
||||||
|
let compact_private_key = CompactPrivateKey::new(pke_params);
|
||||||
|
let ksk = KeySwitchingKey::new((&compact_private_key, None), (&cks, &sk), ksk_params);
|
||||||
|
let pk = CompactPublicKey::new(&compact_private_key);
|
||||||
|
|
||||||
|
let conformance_params =
|
||||||
|
IntegerProvenCompactCiphertextListConformanceParams::from_crs_and_parameters(
|
||||||
|
pke_params, &crs,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut proven_ct = CompactCiphertextList::builder(&pk)
|
||||||
|
.push_with_num_blocks(1u8, 4)
|
||||||
|
.push_with_num_blocks(-1i8, 4)
|
||||||
|
.build_with_proof_packed(&crs, &metadata, ZkComputeLoad::Proof)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert!(proven_ct.is_conformant(&conformance_params));
|
||||||
|
assert_eq!(proven_ct.len(), 2);
|
||||||
|
assert!(proven_ct.is_packed());
|
||||||
|
assert_eq!(proven_ct.ct_list.proved_lists.len(), 2);
|
||||||
|
assert!(proven_ct.ct_list.proved_lists[0].0.is_packed());
|
||||||
|
assert!(proven_ct.ct_list.proved_lists[1].0.is_packed());
|
||||||
|
|
||||||
|
proven_ct.ct_list.proved_lists[0].0.degree = Degree::new(0);
|
||||||
|
assert!(!proven_ct.is_packed());
|
||||||
|
assert_eq!(proven_ct.ct_list.proved_lists.len(), 2);
|
||||||
|
assert!(!proven_ct.ct_list.proved_lists[0].0.is_packed());
|
||||||
|
assert!(proven_ct.ct_list.proved_lists[1].0.is_packed());
|
||||||
|
assert!(!proven_ct.is_conformant(&conformance_params));
|
||||||
|
let expander = proven_ct.verify_and_expand(
|
||||||
|
&crs,
|
||||||
|
&pk,
|
||||||
|
&metadata,
|
||||||
|
IntegerCompactCiphertextListExpansionMode::CastAndUnpackIfNecessary(ksk.as_view()),
|
||||||
|
);
|
||||||
|
assert!(expander.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
/// Test a compact list encryption proven with the v1 zk scheme
|
/// Test a compact list encryption proven with the v1 zk scheme
|
||||||
#[test]
|
#[test]
|
||||||
fn test_zkv1_compact_ciphertext_list_encryption_ci_run_filter() {
|
fn test_zkv1_compact_ciphertext_list_encryption_ci_run_filter() {
|
||||||
@@ -1427,7 +1709,7 @@ mod tests {
|
|||||||
let map_to_fake_boolean = random::<u8>() % 2 == 1;
|
let map_to_fake_boolean = random::<u8>() % 2 == 1;
|
||||||
if map_to_fake_boolean {
|
if map_to_fake_boolean {
|
||||||
if curr_block_count != 0 {
|
if curr_block_count != 0 {
|
||||||
new_infos.push(DataKind::Unsigned(curr_block_count));
|
new_infos.push(DataKind::Unsigned(curr_block_count.try_into().unwrap()));
|
||||||
curr_block_count = 0;
|
curr_block_count = 0;
|
||||||
}
|
}
|
||||||
new_infos.push(DataKind::Boolean);
|
new_infos.push(DataKind::Boolean);
|
||||||
@@ -1436,7 +1718,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if curr_block_count != 0 {
|
if curr_block_count != 0 {
|
||||||
new_infos.push(DataKind::Unsigned(curr_block_count));
|
new_infos.push(DataKind::Unsigned(curr_block_count.try_into().unwrap()));
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -6,40 +6,41 @@ use crate::shortint::ciphertext::CompressedCiphertextList as ShortintCompressedC
|
|||||||
use crate::shortint::Ciphertext;
|
use crate::shortint::Ciphertext;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::num::NonZero;
|
||||||
use tfhe_versionable::Versionize;
|
use tfhe_versionable::Versionize;
|
||||||
|
|
||||||
pub trait Compressible {
|
pub trait Compressible {
|
||||||
fn compress_into(self, messages: &mut Vec<Ciphertext>) -> DataKind;
|
fn compress_into(self, messages: &mut Vec<Ciphertext>) -> Option<DataKind>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Compressible for BooleanBlock {
|
impl Compressible for BooleanBlock {
|
||||||
fn compress_into(self, messages: &mut Vec<Ciphertext>) -> DataKind {
|
fn compress_into(self, messages: &mut Vec<Ciphertext>) -> Option<DataKind> {
|
||||||
messages.push(self.0);
|
messages.push(self.0);
|
||||||
DataKind::Boolean
|
Some(DataKind::Boolean)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Compressible for RadixCiphertext {
|
impl Compressible for RadixCiphertext {
|
||||||
fn compress_into(self, messages: &mut Vec<Ciphertext>) -> DataKind {
|
fn compress_into(self, messages: &mut Vec<Ciphertext>) -> Option<DataKind> {
|
||||||
let num_blocks = self.blocks.len();
|
let num_blocks = self.blocks.len();
|
||||||
|
|
||||||
for block in self.blocks {
|
for block in self.blocks {
|
||||||
messages.push(block);
|
messages.push(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
DataKind::Unsigned(num_blocks)
|
NonZero::new(num_blocks).map(DataKind::Unsigned)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Compressible for SignedRadixCiphertext {
|
impl Compressible for SignedRadixCiphertext {
|
||||||
fn compress_into(self, messages: &mut Vec<Ciphertext>) -> DataKind {
|
fn compress_into(self, messages: &mut Vec<Ciphertext>) -> Option<DataKind> {
|
||||||
let num_blocks = self.blocks.len();
|
let num_blocks = self.blocks.len();
|
||||||
|
|
||||||
for block in self.blocks {
|
for block in self.blocks {
|
||||||
messages.push(block);
|
messages.push(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
DataKind::Signed(num_blocks)
|
NonZero::new(num_blocks).map(DataKind::Signed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,12 +64,38 @@ impl CompressedCiphertextListBuilder {
|
|||||||
T: Compressible,
|
T: Compressible,
|
||||||
{
|
{
|
||||||
let n = self.ciphertexts.len();
|
let n = self.ciphertexts.len();
|
||||||
let kind = data.compress_into(&mut self.ciphertexts);
|
let maybe_kind = data.compress_into(&mut self.ciphertexts);
|
||||||
let num_blocks = self
|
|
||||||
.ciphertexts
|
let Some(modulus) = self.ciphertexts.last().map(|ct| ct.message_modulus) else {
|
||||||
.last()
|
// This means the list of blocks is still empty, so we assert the kind is None
|
||||||
.map_or(0, |ct| kind.num_blocks(ct.message_modulus));
|
// i.e no type pushed, except for strings as we allow empty strings
|
||||||
|
if matches!(maybe_kind, Some(DataKind::String { .. })) {
|
||||||
|
self.info.push(maybe_kind.unwrap());
|
||||||
|
} else {
|
||||||
|
assert!(
|
||||||
|
maybe_kind.is_none(),
|
||||||
|
"Internal error: Incoherent block count with regard to kind"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(kind) = maybe_kind else {
|
||||||
|
assert_eq!(
|
||||||
|
n,
|
||||||
|
self.ciphertexts.len(),
|
||||||
|
"Internal error: Incoherent block count with regard to kind"
|
||||||
|
);
|
||||||
|
return self;
|
||||||
|
};
|
||||||
|
|
||||||
|
let num_blocks = kind.num_blocks(modulus);
|
||||||
|
|
||||||
|
// Check that the number of blocks that were added matches the
|
||||||
|
// number of blocks advertised by the DataKind
|
||||||
assert_eq!(n + num_blocks, self.ciphertexts.len());
|
assert_eq!(n + num_blocks, self.ciphertexts.len());
|
||||||
|
|
||||||
self.info.push(kind);
|
self.info.push(kind);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ use crate::shortint::list_compression::{
|
|||||||
};
|
};
|
||||||
use crate::shortint::parameters::NoiseSquashingCompressionParameters;
|
use crate::shortint::parameters::NoiseSquashingCompressionParameters;
|
||||||
use crate::Versionize;
|
use crate::Versionize;
|
||||||
|
use std::num::NonZero;
|
||||||
|
|
||||||
use crate::integer::backward_compatibility::list_compression::NoiseSquashingCompressionKeyVersions;
|
use crate::integer::backward_compatibility::list_compression::NoiseSquashingCompressionKeyVersions;
|
||||||
|
|
||||||
@@ -228,27 +229,27 @@ mod sealed {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait SquashedNoiseCompressible: sealed::Sealed {
|
pub trait SquashedNoiseCompressible: sealed::Sealed {
|
||||||
fn compress_into(self, messages: &mut Vec<SquashedNoiseCiphertext>) -> DataKind;
|
fn compress_into(self, messages: &mut Vec<SquashedNoiseCiphertext>) -> Option<DataKind>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SquashedNoiseCompressible for SquashedNoiseRadixCiphertext {
|
impl SquashedNoiseCompressible for SquashedNoiseRadixCiphertext {
|
||||||
fn compress_into(mut self, messages: &mut Vec<SquashedNoiseCiphertext>) -> DataKind {
|
fn compress_into(mut self, messages: &mut Vec<SquashedNoiseCiphertext>) -> Option<DataKind> {
|
||||||
messages.append(&mut self.packed_blocks);
|
messages.append(&mut self.packed_blocks);
|
||||||
DataKind::Unsigned(self.original_block_count)
|
NonZero::new(self.original_block_count).map(DataKind::Unsigned)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SquashedNoiseCompressible for SquashedNoiseSignedRadixCiphertext {
|
impl SquashedNoiseCompressible for SquashedNoiseSignedRadixCiphertext {
|
||||||
fn compress_into(mut self, messages: &mut Vec<SquashedNoiseCiphertext>) -> DataKind {
|
fn compress_into(mut self, messages: &mut Vec<SquashedNoiseCiphertext>) -> Option<DataKind> {
|
||||||
messages.append(&mut self.packed_blocks);
|
messages.append(&mut self.packed_blocks);
|
||||||
DataKind::Signed(self.original_block_count)
|
NonZero::new(self.original_block_count).map(DataKind::Signed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SquashedNoiseCompressible for SquashedNoiseBooleanBlock {
|
impl SquashedNoiseCompressible for SquashedNoiseBooleanBlock {
|
||||||
fn compress_into(self, messages: &mut Vec<SquashedNoiseCiphertext>) -> DataKind {
|
fn compress_into(self, messages: &mut Vec<SquashedNoiseCiphertext>) -> Option<DataKind> {
|
||||||
messages.push(self.ciphertext);
|
messages.push(self.ciphertext);
|
||||||
DataKind::Boolean
|
Some(DataKind::Boolean)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,10 +284,13 @@ impl SquashedNoiseExpandable for SquashedNoiseRadixCiphertext {
|
|||||||
if let DataKind::Unsigned(block_count) = kind {
|
if let DataKind::Unsigned(block_count) = kind {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
packed_blocks: blocks,
|
packed_blocks: blocks,
|
||||||
original_block_count: block_count,
|
original_block_count: block_count.get(),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(create_error_message(DataKind::Unsigned(0), kind))
|
Err(create_error_message(
|
||||||
|
DataKind::Unsigned(1.try_into().unwrap()),
|
||||||
|
kind,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -299,10 +303,13 @@ impl SquashedNoiseExpandable for SquashedNoiseSignedRadixCiphertext {
|
|||||||
if let DataKind::Signed(block_count) = kind {
|
if let DataKind::Signed(block_count) = kind {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
packed_blocks: blocks,
|
packed_blocks: blocks,
|
||||||
original_block_count: block_count,
|
original_block_count: block_count.get(),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(create_error_message(DataKind::Signed(0), kind))
|
Err(create_error_message(
|
||||||
|
DataKind::Signed(1.try_into().unwrap()),
|
||||||
|
kind,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -344,15 +351,29 @@ impl CompressedSquashedNoiseCiphertextListBuilder {
|
|||||||
|
|
||||||
pub fn push(&mut self, value: impl SquashedNoiseCompressible) -> &mut Self {
|
pub fn push(&mut self, value: impl SquashedNoiseCompressible) -> &mut Self {
|
||||||
let n = self.list.len();
|
let n = self.list.len();
|
||||||
let kind = value.compress_into(&mut self.list);
|
let maybe_kind = value.compress_into(&mut self.list);
|
||||||
|
|
||||||
|
let Some(modulus) = self.list.last().map(|ct| ct.message_modulus()) else {
|
||||||
|
assert!(
|
||||||
|
maybe_kind.is_none(),
|
||||||
|
"Internal error: Incoherent block count with regard to kind"
|
||||||
|
);
|
||||||
|
return self;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(kind) = maybe_kind else {
|
||||||
|
assert_eq!(
|
||||||
|
n,
|
||||||
|
self.list.len(),
|
||||||
|
"Internal error: Incoherent block count with regard to kind"
|
||||||
|
);
|
||||||
|
return self;
|
||||||
|
};
|
||||||
|
|
||||||
|
let num_blocks = kind.num_blocks(modulus).div_ceil(2); // Because blocks are packed when noise squashed
|
||||||
|
|
||||||
// Check that the number of blocks that were added matches the
|
// Check that the number of blocks that were added matches the
|
||||||
// number of blocks advertised by the DataKind
|
// number of blocks advertised by the DataKind
|
||||||
let num_blocks = self
|
|
||||||
.list
|
|
||||||
.last()
|
|
||||||
.map_or(0, |ct| kind.num_blocks(ct.message_modulus()))
|
|
||||||
.div_ceil(2); // Because blocks are packed when noise squashed
|
|
||||||
assert_eq!(n + num_blocks, self.list.len());
|
assert_eq!(n + num_blocks, self.list.len());
|
||||||
|
|
||||||
self.info.push(kind);
|
self.info.push(kind);
|
||||||
|
|||||||
@@ -2,15 +2,16 @@ use super::{BooleanBlock, IntegerRadixCiphertext};
|
|||||||
use crate::integer::backward_compatibility::ciphertext::DataKindVersions;
|
use crate::integer::backward_compatibility::ciphertext::DataKindVersions;
|
||||||
use crate::shortint::{Ciphertext, MessageModulus};
|
use crate::shortint::{Ciphertext, MessageModulus};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::num::NonZeroUsize;
|
||||||
use tfhe_versionable::Versionize;
|
use tfhe_versionable::Versionize;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Versionize)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Versionize)]
|
||||||
#[versionize(DataKindVersions)]
|
#[versionize(DataKindVersions)]
|
||||||
pub enum DataKind {
|
pub enum DataKind {
|
||||||
/// The held value is a number of radix blocks.
|
/// The held value is a number of radix blocks.
|
||||||
Unsigned(usize),
|
Unsigned(NonZeroUsize),
|
||||||
/// The held value is a number of radix blocks.
|
/// The held value is a number of radix blocks.
|
||||||
Signed(usize),
|
Signed(NonZeroUsize),
|
||||||
Boolean,
|
Boolean,
|
||||||
String {
|
String {
|
||||||
n_chars: u32,
|
n_chars: u32,
|
||||||
@@ -21,7 +22,7 @@ pub enum DataKind {
|
|||||||
impl DataKind {
|
impl DataKind {
|
||||||
pub fn num_blocks(self, message_modulus: MessageModulus) -> usize {
|
pub fn num_blocks(self, message_modulus: MessageModulus) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Self::Unsigned(n) | Self::Signed(n) => n,
|
Self::Unsigned(n) | Self::Signed(n) => n.get(),
|
||||||
Self::Boolean => 1,
|
Self::Boolean => 1,
|
||||||
Self::String { n_chars, .. } => {
|
Self::String { n_chars, .. } => {
|
||||||
let blocks_per_char = 7u32.div_ceil(message_modulus.0.ilog2());
|
let blocks_per_char = 7u32.div_ceil(message_modulus.0.ilog2());
|
||||||
|
|||||||
@@ -248,8 +248,8 @@ impl CudaFlattenedVecCompactCiphertextList {
|
|||||||
.flat_map(|data_kind| {
|
.flat_map(|data_kind| {
|
||||||
let repetitions = match data_kind {
|
let repetitions = match data_kind {
|
||||||
DataKind::Boolean => 1,
|
DataKind::Boolean => 1,
|
||||||
DataKind::Signed(x) => *x,
|
DataKind::Signed(x) => x.get(),
|
||||||
DataKind::Unsigned(x) => *x,
|
DataKind::Unsigned(x) => x.get(),
|
||||||
DataKind::String { .. } => panic!("DataKind not supported on GPUs"),
|
DataKind::String { .. } => panic!("DataKind not supported on GPUs"),
|
||||||
};
|
};
|
||||||
std::iter::repeat_n(matches!(data_kind, DataKind::Boolean), repetitions)
|
std::iter::repeat_n(matches!(data_kind, DataKind::Boolean), repetitions)
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ use crate::shortint::parameters::AtomicPatternKind;
|
|||||||
use crate::shortint::PBSOrder;
|
use crate::shortint::PBSOrder;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use serde::{Deserializer, Serializer};
|
use serde::{Deserializer, Serializer};
|
||||||
|
use std::num::NonZeroUsize;
|
||||||
|
|
||||||
pub trait CudaExpandable: Sized {
|
pub trait CudaExpandable: Sized {
|
||||||
fn from_expanded_blocks(blocks: CudaRadixCiphertext, kind: DataKind) -> crate::Result<Self>;
|
fn from_expanded_blocks(blocks: CudaRadixCiphertext, kind: DataKind) -> crate::Result<Self>;
|
||||||
@@ -447,7 +448,7 @@ pub trait CudaCompressible {
|
|||||||
self,
|
self,
|
||||||
messages: &mut Vec<CudaRadixCiphertext>,
|
messages: &mut Vec<CudaRadixCiphertext>,
|
||||||
streams: &CudaStreams,
|
streams: &CudaStreams,
|
||||||
) -> DataKind;
|
) -> Option<DataKind>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CudaCompressible for CudaSignedRadixCiphertext {
|
impl CudaCompressible for CudaSignedRadixCiphertext {
|
||||||
@@ -455,12 +456,15 @@ impl CudaCompressible for CudaSignedRadixCiphertext {
|
|||||||
self,
|
self,
|
||||||
messages: &mut Vec<CudaRadixCiphertext>,
|
messages: &mut Vec<CudaRadixCiphertext>,
|
||||||
streams: &CudaStreams,
|
streams: &CudaStreams,
|
||||||
) -> DataKind {
|
) -> Option<DataKind> {
|
||||||
let x = self.ciphertext.duplicate(streams);
|
let x = self.ciphertext.duplicate(streams);
|
||||||
let num_blocks = x.d_blocks.lwe_ciphertext_count().0;
|
let num_blocks = x.d_blocks.lwe_ciphertext_count().0;
|
||||||
|
|
||||||
messages.push(x);
|
let num_blocks = NonZeroUsize::new(num_blocks);
|
||||||
DataKind::Signed(num_blocks)
|
if num_blocks.is_some() {
|
||||||
|
messages.push(x)
|
||||||
|
}
|
||||||
|
num_blocks.map(DataKind::Signed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -469,11 +473,11 @@ impl CudaCompressible for CudaBooleanBlock {
|
|||||||
self,
|
self,
|
||||||
messages: &mut Vec<CudaRadixCiphertext>,
|
messages: &mut Vec<CudaRadixCiphertext>,
|
||||||
streams: &CudaStreams,
|
streams: &CudaStreams,
|
||||||
) -> DataKind {
|
) -> Option<DataKind> {
|
||||||
let x = self.0.ciphertext.duplicate(streams);
|
let x = self.0.ciphertext.duplicate(streams);
|
||||||
|
|
||||||
messages.push(x);
|
messages.push(x);
|
||||||
DataKind::Boolean
|
Some(DataKind::Boolean)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl CudaCompressible for CudaUnsignedRadixCiphertext {
|
impl CudaCompressible for CudaUnsignedRadixCiphertext {
|
||||||
@@ -481,12 +485,15 @@ impl CudaCompressible for CudaUnsignedRadixCiphertext {
|
|||||||
self,
|
self,
|
||||||
messages: &mut Vec<CudaRadixCiphertext>,
|
messages: &mut Vec<CudaRadixCiphertext>,
|
||||||
streams: &CudaStreams,
|
streams: &CudaStreams,
|
||||||
) -> DataKind {
|
) -> Option<DataKind> {
|
||||||
let x = self.ciphertext.duplicate(streams);
|
let x = self.ciphertext.duplicate(streams);
|
||||||
let num_blocks = x.d_blocks.lwe_ciphertext_count().0;
|
let num_blocks = x.d_blocks.lwe_ciphertext_count().0;
|
||||||
|
|
||||||
messages.push(x);
|
let num_blocks = NonZeroUsize::new(num_blocks);
|
||||||
DataKind::Unsigned(num_blocks)
|
if num_blocks.is_some() {
|
||||||
|
messages.push(x)
|
||||||
|
}
|
||||||
|
num_blocks.map(DataKind::Unsigned)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -505,13 +512,9 @@ impl CudaCompressedCiphertextListBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn push<T: CudaCompressible>(&mut self, data: T, streams: &CudaStreams) -> &mut Self {
|
pub fn push<T: CudaCompressible>(&mut self, data: T, streams: &CudaStreams) -> &mut Self {
|
||||||
let kind = data.compress_into(&mut self.ciphertexts, streams);
|
if let Some(kind) = data.compress_into(&mut self.ciphertexts, streams) {
|
||||||
let message_modulus = self.ciphertexts.last().unwrap().info.blocks[0].message_modulus;
|
|
||||||
|
|
||||||
if kind.num_blocks(message_modulus) != 0 {
|
|
||||||
self.info.push(kind);
|
self.info.push(kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -155,6 +155,7 @@ mod tests {
|
|||||||
ProvenCompactCiphertextList,
|
ProvenCompactCiphertextList,
|
||||||
};
|
};
|
||||||
use crate::shortint::parameters::test_params::TEST_PARAM_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128_ZKV2;
|
use crate::shortint::parameters::test_params::TEST_PARAM_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128_ZKV2;
|
||||||
|
use std::num::NonZero;
|
||||||
// TODO test params update for the v1_3
|
// TODO test params update for the v1_3
|
||||||
use crate::shortint::parameters::current_params::V1_3_PARAM_KEYSWITCH_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
|
use crate::shortint::parameters::current_params::V1_3_PARAM_KEYSWITCH_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
|
||||||
use crate::shortint::parameters::{
|
use crate::shortint::parameters::{
|
||||||
@@ -426,8 +427,8 @@ mod tests {
|
|||||||
for _ in 0..infos_block_count {
|
for _ in 0..infos_block_count {
|
||||||
let map_to_fake_boolean = random::<u8>() % 2 == 1;
|
let map_to_fake_boolean = random::<u8>() % 2 == 1;
|
||||||
if map_to_fake_boolean {
|
if map_to_fake_boolean {
|
||||||
if curr_block_count != 0 {
|
if let Some(count) = NonZero::new(curr_block_count) {
|
||||||
new_infos.push(DataKind::Unsigned(curr_block_count));
|
new_infos.push(DataKind::Unsigned(count));
|
||||||
curr_block_count = 0;
|
curr_block_count = 0;
|
||||||
}
|
}
|
||||||
new_infos.push(DataKind::Boolean);
|
new_infos.push(DataKind::Boolean);
|
||||||
@@ -435,8 +436,8 @@ mod tests {
|
|||||||
curr_block_count += 1;
|
curr_block_count += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if curr_block_count != 0 {
|
if let Some(count) = NonZero::new(curr_block_count) {
|
||||||
new_infos.push(DataKind::Unsigned(curr_block_count));
|
new_infos.push(DataKind::Unsigned(count));
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -228,4 +228,8 @@ impl CompactCiphertextList {
|
|||||||
pub fn size_bytes(&self) -> usize {
|
pub fn size_bytes(&self) -> usize {
|
||||||
self.ct_list.size_bytes()
|
self.ct_list.size_bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_packed(&self) -> bool {
|
||||||
|
self.degree.get() > self.message_modulus.corresponding_max_degree().get()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ impl Compactable for &ClearString {
|
|||||||
messages: &mut Vec<u64>,
|
messages: &mut Vec<u64>,
|
||||||
message_modulus: MessageModulus,
|
message_modulus: MessageModulus,
|
||||||
num_blocks: Option<usize>,
|
num_blocks: Option<usize>,
|
||||||
) -> crate::integer::ciphertext::DataKind {
|
) -> Option<DataKind> {
|
||||||
let blocks_per_char = 7u32.div_ceil(message_modulus.0.ilog2());
|
let blocks_per_char = 7u32.div_ceil(message_modulus.0.ilog2());
|
||||||
|
|
||||||
if let Some(n) = num_blocks {
|
if let Some(n) = num_blocks {
|
||||||
@@ -79,7 +79,7 @@ impl Compactable for &ClearString {
|
|||||||
n_blocks / blocks_per_char as usize
|
n_blocks / blocks_per_char as usize
|
||||||
});
|
});
|
||||||
|
|
||||||
// First write the chars we have at hand
|
// First, write the chars we have at hand
|
||||||
let n_real_chars = n_chars.min(self.str().len());
|
let n_real_chars = n_chars.min(self.str().len());
|
||||||
for byte in &self.str.as_bytes()[..n_real_chars] {
|
for byte in &self.str.as_bytes()[..n_real_chars] {
|
||||||
let mut byte = u64::from(*byte);
|
let mut byte = u64::from(*byte);
|
||||||
@@ -95,10 +95,10 @@ impl Compactable for &ClearString {
|
|||||||
messages.push(0);
|
messages.push(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
DataKind::String {
|
Some(DataKind::String {
|
||||||
n_chars: n_chars as u32,
|
n_chars: n_chars as u32,
|
||||||
padded,
|
padded,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,13 +110,24 @@ impl crate::integer::ciphertext::CompactCiphertextListBuilder {
|
|||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
let message_modulus = self.pk.key.message_modulus();
|
let message_modulus = self.pk.key.message_modulus();
|
||||||
let blocks_per_char = 7u32.div_ceil(message_modulus.0.ilog2());
|
let blocks_per_char = 7u32.div_ceil(message_modulus.0.ilog2());
|
||||||
|
let n = self.messages.len();
|
||||||
|
|
||||||
let kind = clear_string.compact_into(
|
let kind = clear_string
|
||||||
&mut self.messages,
|
.compact_into(
|
||||||
message_modulus,
|
&mut self.messages,
|
||||||
Some((clear_string.str.len() + padding_count as usize) * blocks_per_char as usize),
|
message_modulus,
|
||||||
);
|
Some((clear_string.str.len() + padding_count as usize) * blocks_per_char as usize),
|
||||||
|
)
|
||||||
|
.expect("Internal error: compact_into should return a kind");
|
||||||
self.info.push(kind);
|
self.info.push(kind);
|
||||||
|
|
||||||
|
let added_count = kind.num_blocks(message_modulus);
|
||||||
|
assert_eq!(
|
||||||
|
n + added_count,
|
||||||
|
self.messages.len(),
|
||||||
|
"Internal error: Incoherent number of blocks added"
|
||||||
|
);
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,13 +138,24 @@ impl crate::integer::ciphertext::CompactCiphertextListBuilder {
|
|||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
let message_modulus = self.pk.key.message_modulus();
|
let message_modulus = self.pk.key.message_modulus();
|
||||||
let blocks_per_char = 7u32.div_ceil(message_modulus.0.ilog2());
|
let blocks_per_char = 7u32.div_ceil(message_modulus.0.ilog2());
|
||||||
|
let n = self.messages.len();
|
||||||
|
|
||||||
let kind = clear_string.compact_into(
|
let kind = clear_string
|
||||||
&mut self.messages,
|
.compact_into(
|
||||||
message_modulus,
|
&mut self.messages,
|
||||||
Some((size * blocks_per_char) as usize),
|
message_modulus,
|
||||||
);
|
Some((size * blocks_per_char) as usize),
|
||||||
|
)
|
||||||
|
.expect("Internal error: compact_into should return a kind");
|
||||||
self.info.push(kind);
|
self.info.push(kind);
|
||||||
|
|
||||||
|
let added_count = kind.num_blocks(message_modulus);
|
||||||
|
assert_eq!(
|
||||||
|
n + added_count,
|
||||||
|
self.messages.len(),
|
||||||
|
"Internal error: Incoherent number of blocks added"
|
||||||
|
);
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -186,7 +208,7 @@ impl crate::integer::ciphertext::Expandable for FheString {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl crate::integer::ciphertext::Compressible for FheString {
|
impl crate::integer::ciphertext::Compressible for FheString {
|
||||||
fn compress_into(self, messages: &mut Vec<crate::shortint::Ciphertext>) -> DataKind {
|
fn compress_into(self, messages: &mut Vec<crate::shortint::Ciphertext>) -> Option<DataKind> {
|
||||||
let n_chars = self.chars().len() as u32;
|
let n_chars = self.chars().len() as u32;
|
||||||
let padded = self.is_padded();
|
let padded = self.is_padded();
|
||||||
|
|
||||||
@@ -196,7 +218,7 @@ impl crate::integer::ciphertext::Compressible for FheString {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DataKind::String { n_chars, padded }
|
Some(DataKind::String { n_chars, padded })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -318,7 +318,7 @@ impl Named for SerializableCompactPkePublicParams {
|
|||||||
const NAME: &'static str = ZkCompactPkeV1PublicParams::NAME;
|
const NAME: &'static str = ZkCompactPkeV1PublicParams::NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||||
pub enum ZkVerificationOutcome {
|
pub enum ZkVerificationOutcome {
|
||||||
/// The proof and its entity were valid
|
/// The proof and its entity were valid
|
||||||
Valid,
|
Valid,
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ use std::convert::Infallible;
|
|||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::num::Wrapping;
|
use std::num::{NonZero, Wrapping};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub use derived_traits::{Version, VersionsDispatch};
|
pub use derived_traits::{Version, VersionsDispatch};
|
||||||
@@ -268,6 +268,9 @@ impl_scalar_versionize!(f64);
|
|||||||
|
|
||||||
impl_scalar_versionize!(char);
|
impl_scalar_versionize!(char);
|
||||||
|
|
||||||
|
impl_scalar_versionize!(NonZero<u32>);
|
||||||
|
impl_scalar_versionize!(NonZero<usize>);
|
||||||
|
|
||||||
impl<T: Versionize> Versionize for Wrapping<T> {
|
impl<T: Versionize> Versionize for Wrapping<T> {
|
||||||
type Versioned<'vers>
|
type Versioned<'vers>
|
||||||
= Wrapping<T::Versioned<'vers>>
|
= Wrapping<T::Versioned<'vers>>
|
||||||
|
|||||||
Reference in New Issue
Block a user