feat(high_level_api): add casting primitives for compact public key

This commit is contained in:
Arthur Meyre
2024-06-14 23:20:17 +02:00
parent bd674fe5bc
commit 9242b2a725
41 changed files with 1606 additions and 496 deletions

View File

@@ -273,7 +273,7 @@ clippy_c_api: install_rs_check_toolchain
.PHONY: clippy_js_wasm_api # Run clippy lints enabling the boolean, shortint, integer and the js wasm API
clippy_js_wasm_api: install_rs_check_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" clippy \
--features=boolean-client-js-wasm-api,shortint-client-js-wasm-api,integer-client-js-wasm-api \
--features=boolean-client-js-wasm-api,shortint-client-js-wasm-api,integer-client-js-wasm-api,high-level-client-js-wasm-api \
-p $(TFHE_SPEC) -- --no-deps -D warnings
.PHONY: clippy_tasks # Run clippy lints on helper tasks crate.

View File

@@ -45,6 +45,11 @@ if [[ "${CARGO_PROFILE}" == "" ]]; then
exit 1
fi
if [[ "${CARGO_PROFILE}" == "dev" ]]; then
# dev gets remapped
CARGO_PROFILE="debug"
fi
UNAME="$(uname)"
if [[ "${UNAME}" != "Linux" && "${UNAME}" != "Darwin" ]]; then
echo "This script is compatible with Linux and macOS and may not work for your system"

View File

@@ -108,12 +108,8 @@ bench_type!(FheUint64);
bench_type!(FheUint128);
fn main() {
let config = ConfigBuilder::with_custom_parameters(
// PARAM_MULTI_BIT_MESSAGE_2_CARRY_2_GROUP_3_KS_PBS,
PARAM_MESSAGE_2_CARRY_2_KS_PBS,
None,
)
.build();
let config =
ConfigBuilder::with_custom_parameters(PARAM_MESSAGE_2_CARRY_2_KS_PBS, None).build();
let cks = ClientKey::generate(config);
let compressed_sks = CompressedServerKey::new(&cks);

View File

@@ -8,37 +8,19 @@ use std::fs::{File, OpenOptions};
use std::io::Write;
use std::path::Path;
use tfhe::core_crypto::prelude::*;
use tfhe::integer::key_switching_key::KeySwitchingKey;
use tfhe::integer::parameters::{
IntegerCompactCiphertextListCastingMode, IntegerCompactCiphertextListUnpackingMode,
};
use tfhe::integer::{ClientKey, CompactPublicKey, ServerKey};
use tfhe::shortint::ciphertext::MaxNoiseLevel;
use tfhe::shortint::parameters::{
CarryModulus, ClassicPBSParameters, MessageModulus, PBSParameters,
};
use tfhe::integer::{ClientKey, CompactPrivateKey, CompactPublicKey, ServerKey};
use tfhe::keycache::NamedParam;
use tfhe::shortint::parameters::classic::tuniform::p_fail_2_minus_64::ks_pbs::PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
use tfhe::shortint::parameters::compact_public_key_only::PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
use tfhe::shortint::parameters::key_switching::PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
use tfhe::shortint::parameters::PBSParameters;
use tfhe::zk::{CompactPkeCrs, ZkComputeLoad};
use utilities::{write_to_json, OperatorType};
// TODO to remove once casting is available
pub const PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS_TUNIFORM_2M64: ClassicPBSParameters =
ClassicPBSParameters {
lwe_dimension: LweDimension(1024),
glwe_dimension: GlweDimension(1),
polynomial_size: PolynomialSize(2048),
lwe_noise_distribution: DynamicDistribution::new_t_uniform(41),
glwe_noise_distribution: DynamicDistribution::new_t_uniform(14),
pbs_base_log: DecompositionBaseLog(23),
pbs_level: DecompositionLevelCount(1),
ks_base_log: DecompositionBaseLog(5),
ks_level: DecompositionLevelCount(4),
message_modulus: MessageModulus(4),
carry_modulus: CarryModulus(4),
max_noise_level: MaxNoiseLevel::new(5),
log2_p_fail: -66.873,
ciphertext_modulus: CiphertextModulus::new_native(),
encryption_key_choice: EncryptionKeyChoice::Small,
};
fn write_result(file: &mut File, name: &str, value: usize) {
let line = format!("{name},{value}\n");
let error_message = format!("cannot write {name} result into file");
@@ -51,12 +33,20 @@ fn pke_zk_proof(c: &mut Criterion) {
.sample_size(15)
.measurement_time(std::time::Duration::from_secs(60));
for (param_name, param_pke) in [(
"PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS_TUNIFORM_2M64",
PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS_TUNIFORM_2M64,
for (param_pke, _param_casting, param_fhe) in [(
PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64,
PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64,
PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64,
)] {
let cks_pke = ClientKey::new(param_pke);
let pk = CompactPublicKey::new(&cks_pke);
let param_name = param_fhe.name();
let param_name = param_name.as_str();
let cks = ClientKey::new(param_fhe);
let sks = ServerKey::new_radix_server_key(&cks);
let compact_private_key = CompactPrivateKey::new(param_pke);
let pk = CompactPublicKey::new(&compact_private_key);
// Kept for consistency
let _casting_key =
KeySwitchingKey::new((&compact_private_key, None), (&cks, &sks), _param_casting);
for bits in [640usize, 1280, 4096] {
assert_eq!(bits % 64, 0);
@@ -88,7 +78,7 @@ fn pke_zk_proof(c: &mut Criterion) {
})
});
let shortint_params: PBSParameters = param_pke.into();
let shortint_params: PBSParameters = param_fhe.into();
write_to_json::<u64, _>(
&bench_id,
@@ -120,13 +110,19 @@ fn pke_zk_verify(c: &mut Criterion, results_file: &Path) {
.open(results_file)
.expect("cannot open results file");
for (param_name, param_pke) in [(
"PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS_TUNIFORM_2M64",
PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS_TUNIFORM_2M64,
for (param_pke, param_casting, param_fhe) in [(
PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64,
PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64,
PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64,
)] {
let cks_pke = ClientKey::new(param_pke);
let pk = CompactPublicKey::new(&cks_pke);
let sks = ServerKey::new_radix_server_key(&cks_pke);
let param_name = param_fhe.name();
let param_name = param_name.as_str();
let cks = ClientKey::new(param_fhe);
let sks = ServerKey::new_radix_server_key(&cks);
let compact_private_key = CompactPrivateKey::new(param_pke);
let pk = CompactPublicKey::new(&compact_private_key);
let casting_key =
KeySwitchingKey::new((&compact_private_key, None), (&cks, &sks), param_casting);
for bits in [640usize, 1280, 4096] {
assert_eq!(bits % 64, 0);
@@ -142,7 +138,7 @@ fn pke_zk_verify(c: &mut Criterion, results_file: &Path) {
CompactPkeCrs::from_shortint_params(param_pke, num_block * fhe_uint_count).unwrap();
let public_params = crs.public_params();
let shortint_params: PBSParameters = param_pke.into();
let shortint_params: PBSParameters = param_fhe.into();
let mut crs_data = vec![];
public_params
@@ -183,7 +179,9 @@ fn pke_zk_verify(c: &mut Criterion, results_file: &Path) {
public_params,
&pk,
IntegerCompactCiphertextListUnpackingMode::UnpackIfNecessary(&sks),
IntegerCompactCiphertextListCastingMode::NoCasting,
IntegerCompactCiphertextListCastingMode::CastIfNecessary(
casting_key.as_view(),
),
)
.unwrap();
});

View File

@@ -5,21 +5,18 @@
#include <stdint.h>
#include <stdlib.h>
int main(void) {
int cpk_use_case(Config *config) {
int ok = 0;
// First, we create a ClientKey and a CompactPublicKey
ClientKey *client_key = NULL;
ServerKey *server_key = NULL;
CompactPublicKey *public_key = NULL;
{
ConfigBuilder *builder;
Config *config;
ok = config_builder_default(&builder);
assert(ok == 0);
ok = config_builder_build(builder, &config);
ok = generate_keys(config, &client_key, &server_key);
assert(ok == 0);
ok = client_key_generate(config, &client_key);
ok = set_server_key(server_key);
assert(ok == 0);
ok = compact_public_key_new(client_key, &public_key);
@@ -66,20 +63,20 @@ int main(void) {
assert(ok == 0);
size_t len = 0;
ok = compact_ciphertext_list_expander_len(expander, &len);
ok = compact_ciphertext_list_expander_len(expander, &len);
assert(ok == 0 && len == 4);
// First, an example of getting the type in a slot
ok = compact_ciphertext_list_expander_get_kind_of(expander, 0, &type);
ok = compact_ciphertext_list_expander_get_kind_of(expander, 0, &type);
assert(ok == 0 && type == Type_FheUint32);
ok = compact_ciphertext_list_expander_get_kind_of(expander, 1, &type);
ok = compact_ciphertext_list_expander_get_kind_of(expander, 1, &type);
assert(ok == 0 && type == Type_FheInt64);
ok = compact_ciphertext_list_expander_get_kind_of(expander, 2, &type);
ok = compact_ciphertext_list_expander_get_kind_of(expander, 2, &type);
assert(ok == 0 && type == Type_FheBool);
ok = compact_ciphertext_list_expander_get_kind_of(expander, 3, &type);
ok = compact_ciphertext_list_expander_get_kind_of(expander, 3, &type);
assert(ok == 0 && type == Type_FheUint2);
// Then how to get the values
@@ -124,7 +121,41 @@ int main(void) {
fhe_bool_destroy(c);
fhe_uint2_destroy(d);
client_key_destroy(client_key);
server_key_destroy(server_key);
compact_public_key_destroy(public_key);
compact_ciphertext_list_destroy(compact_list);
return ok;
}
int main(void) {
int ok = 0;
{
ConfigBuilder *builder;
Config *config;
ok = config_builder_default(&builder);
assert(ok == 0);
ok = config_builder_build(builder, &config);
assert(ok == 0);
int ok = cpk_use_case(config);
assert(ok == 0);
}
{
ConfigBuilder *builder;
Config *config;
ok = config_builder_default(&builder);
assert(ok == 0);
ok = config_builder_use_custom_parameters(
&builder, SHORTINT_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64);
assert(ok == 0);
ok = use_dedicated_compact_public_key_parameters(
&builder, SHORTINT_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64);
assert(ok == 0);
ok = config_builder_build(builder, &config);
assert(ok == 0);
int ok = cpk_use_case(config);
assert(ok == 0);
}
return EXIT_SUCCESS;
}

View File

@@ -8,46 +8,21 @@ use std::fs;
use std::fs::{File, OpenOptions};
use std::io::Write;
use std::path::Path;
use tfhe::core_crypto::prelude::CiphertextModulus;
use tfhe::keycache::NamedParam;
use tfhe::shortint::keycache::{
PARAM_MESSAGE_1_CARRY_1_KS_PBS_NAME, PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS_NAME,
PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS_NAME, PARAM_MESSAGE_2_CARRY_2_KS_PBS_NAME,
PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64_NAME,
};
use tfhe::shortint::parameters::classic::compact_pk::{
PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS, PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS,
};
use tfhe::shortint::parameters::{
DecompositionBaseLog, DecompositionLevelCount, DynamicDistribution, GlweDimension,
LweDimension, PolynomialSize, PARAM_MESSAGE_1_CARRY_1_KS_PBS, PARAM_MESSAGE_2_CARRY_2_KS_PBS,
};
use tfhe::shortint::{
CarryModulus, ClassicPBSParameters, EncryptionKeyChoice, MaxNoiseLevel, MessageModulus,
PBSParameters,
};
use tfhe::shortint::parameters::classic::tuniform::p_fail_2_minus_64::ks_pbs::PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
use tfhe::shortint::parameters::{PARAM_MESSAGE_1_CARRY_1_KS_PBS, PARAM_MESSAGE_2_CARRY_2_KS_PBS};
use tfhe::shortint::{ClassicPBSParameters, PBSParameters};
const BENCHMARK_NAME_PREFIX: &str = "wasm::";
// TODO To remove once casting is available
pub const PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS_TUNIFORM_2M64: ClassicPBSParameters =
ClassicPBSParameters {
lwe_dimension: LweDimension(1024),
glwe_dimension: GlweDimension(1),
polynomial_size: PolynomialSize(2048),
lwe_noise_distribution: DynamicDistribution::new_t_uniform(41),
glwe_noise_distribution: DynamicDistribution::new_t_uniform(14),
pbs_base_log: DecompositionBaseLog(23),
pbs_level: DecompositionLevelCount(1),
ks_base_log: DecompositionBaseLog(5),
ks_level: DecompositionLevelCount(4),
message_modulus: MessageModulus(4),
carry_modulus: CarryModulus(4),
max_noise_level: MaxNoiseLevel::new(5),
log2_p_fail: -66.873,
ciphertext_modulus: CiphertextModulus::new_native(),
encryption_key_choice: EncryptionKeyChoice::Small,
};
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
@@ -60,8 +35,8 @@ fn params_from_name(name: &str) -> ClassicPBSParameters {
PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS_NAME => PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS,
PARAM_MESSAGE_1_CARRY_1_KS_PBS_NAME => PARAM_MESSAGE_1_CARRY_1_KS_PBS,
PARAM_MESSAGE_2_CARRY_2_KS_PBS_NAME => PARAM_MESSAGE_2_CARRY_2_KS_PBS,
"PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS_TUNIFORM_2M64" => {
PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS_TUNIFORM_2M64
PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64_NAME => {
PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64
}
_ => panic!("failed to get parameters for name '{name}'"),
}

View File

@@ -67,6 +67,7 @@ pub unsafe extern "C" fn config_builder_use_custom_parameters(
let params: crate::shortint::ClassicPBSParameters =
shortint_block_parameters.try_into().unwrap();
let inner = Box::from_raw(*builder)
.0
.use_custom_parameters(params, None);
@@ -74,6 +75,23 @@ pub unsafe extern "C" fn config_builder_use_custom_parameters(
})
}
#[no_mangle]
pub unsafe extern "C" fn use_dedicated_compact_public_key_parameters(
builder: *mut *mut ConfigBuilder,
compact_public_key_parameters: crate::c_api::shortint::parameters::ShortintCompactPublicKeyEncryptionParameters,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(builder).unwrap();
let inner = Box::from_raw(*builder)
.0
.use_dedicated_compact_public_key_parameters(
compact_public_key_parameters.try_into().unwrap(),
);
*builder = Box::into_raw(Box::new(ConfigBuilder(inner)));
})
}
/// Takes ownership of the builder
#[no_mangle]
pub unsafe extern "C" fn config_builder_build(

View File

@@ -3,6 +3,8 @@ pub use crate::core_crypto::commons::dispersion::StandardDev;
pub use crate::core_crypto::commons::parameters::{
DecompositionBaseLog, DecompositionLevelCount, GlweDimension, LweDimension, PolynomialSize,
};
pub use crate::shortint::parameters::compact_public_key_only::PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
pub use crate::shortint::parameters::key_switching::PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
pub use crate::shortint::parameters::*;
use std::os::raw::c_int;
@@ -123,6 +125,132 @@ impl ShortintPBSParameters {
}
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct ShortintCompactCiphertextListCastingParameters {
pub ks_base_log: usize,
pub ks_level: usize,
pub destination_key: ShortintEncryptionKeyChoice,
}
impl From<ShortintCompactCiphertextListCastingParameters>
for crate::shortint::parameters::key_switching::ShortintKeySwitchingParameters
{
fn from(value: ShortintCompactCiphertextListCastingParameters) -> Self {
Self {
ks_base_log: DecompositionBaseLog(value.ks_base_log),
ks_level: DecompositionLevelCount(value.ks_level),
destination_key: value.destination_key.into(),
}
}
}
impl From<crate::shortint::parameters::key_switching::ShortintKeySwitchingParameters>
for ShortintCompactCiphertextListCastingParameters
{
fn from(
rust_params: crate::shortint::parameters::key_switching::ShortintKeySwitchingParameters,
) -> Self {
Self::convert(rust_params)
}
}
impl ShortintCompactCiphertextListCastingParameters {
const fn convert(
rust_params: crate::shortint::parameters::key_switching::ShortintKeySwitchingParameters,
) -> Self {
Self {
ks_base_log: rust_params.ks_base_log.0,
ks_level: rust_params.ks_level.0,
destination_key: ShortintEncryptionKeyChoice::convert(rust_params.destination_key),
}
}
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct ShortintCompactPublicKeyEncryptionParameters {
pub encryption_lwe_dimension: usize,
pub encryption_noise_distribution: crate::c_api::core_crypto::DynamicDistribution,
pub message_modulus: usize,
pub carry_modulus: usize,
pub modulus_power_of_2_exponent: usize,
// Normally the CompactPublicKeyEncryptionParameters has an additional field expansion_kind,
// but it's only used to manage different kind of parameters internally, for the C API
// these parameters will always require casting, as they alwasy require casting we add a field
// for the casting parameters here.
pub casting_parameters: ShortintCompactCiphertextListCastingParameters,
}
impl TryFrom<ShortintCompactPublicKeyEncryptionParameters>
for crate::shortint::parameters::CompactPublicKeyEncryptionParameters
{
type Error = &'static str;
fn try_from(
c_params: ShortintCompactPublicKeyEncryptionParameters,
) -> Result<Self, Self::Error> {
Ok(Self {
encryption_lwe_dimension: LweDimension(c_params.encryption_lwe_dimension),
encryption_noise_distribution: c_params.encryption_noise_distribution.try_into()?,
message_modulus: MessageModulus(c_params.message_modulus),
carry_modulus: CarryModulus(c_params.carry_modulus),
ciphertext_modulus: crate::shortint::parameters::CiphertextModulus::try_new_power_of_2(
c_params.modulus_power_of_2_exponent,
)?,
expansion_kind:
crate::shortint::parameters::CompactCiphertextListExpansionKind::RequiresCasting,
})
}
}
impl TryFrom<ShortintCompactPublicKeyEncryptionParameters>
for (
crate::shortint::parameters::CompactPublicKeyEncryptionParameters,
crate::shortint::parameters::key_switching::ShortintKeySwitchingParameters,
)
{
type Error = &'static str;
fn try_from(value: ShortintCompactPublicKeyEncryptionParameters) -> Result<Self, Self::Error> {
Ok((value.try_into()?, value.casting_parameters.into()))
}
}
impl ShortintCompactPublicKeyEncryptionParameters {
const fn convert(
rust_params: (
crate::shortint::parameters::CompactPublicKeyEncryptionParameters,
crate::shortint::parameters::key_switching::ShortintKeySwitchingParameters,
),
) -> Self {
let compact_pke_params = rust_params.0;
let casting_parameters = rust_params.1;
Self {
encryption_lwe_dimension: compact_pke_params.encryption_lwe_dimension.0,
encryption_noise_distribution: compact_pke_params
.encryption_noise_distribution
.convert_to_c(),
message_modulus: compact_pke_params.message_modulus.0,
carry_modulus: compact_pke_params.carry_modulus.0,
modulus_power_of_2_exponent: convert_modulus(compact_pke_params.ciphertext_modulus),
casting_parameters: ShortintCompactCiphertextListCastingParameters::convert(
casting_parameters,
),
}
}
}
// TODO: use macros once we have more parameters using the same pattern as
// expose_predefined_parameters
#[no_mangle]
pub static SHORTINT_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64:
ShortintCompactPublicKeyEncryptionParameters =
ShortintCompactPublicKeyEncryptionParameters::convert((
PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64,
PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64,
));
macro_rules! expose_as_shortint_pbs_parameters(
(
$(

View File

@@ -57,3 +57,11 @@ impl From<String> for Error {
}
impl std::error::Error for Error {}
// This is useful to use infallible conversions as well as fallible ones in certain parts of the lib
impl From<std::convert::Infallible> for Error {
fn from(_value: std::convert::Infallible) -> Self {
// This can never be reached
unreachable!()
}
}

View File

@@ -35,7 +35,6 @@ impl CompactCiphertextList {
) -> crate::Result<CompactCiphertextListExpander> {
self.0
.expand(
// TODO check packing and casting modes
IntegerCompactCiphertextListUnpackingMode::UnpackIfNecessary(sks.key.pbs_key()),
IntegerCompactCiphertextListCastingMode::NoCasting,
)
@@ -64,7 +63,15 @@ impl CompactCiphertextList {
};
let casting_mode = if self.0.needs_casting() {
todo!("TODO check packing and casting modes")
IntegerCompactCiphertextListCastingMode::CastIfNecessary(
cpu_key.cpk_casting_key().ok_or_else(|| {
crate::Error::new(
"No casting key found in ServerKey, \
required to expand this CompactCiphertextList"
.to_string(),
)
})?,
)
} else {
IntegerCompactCiphertextListCastingMode::NoCasting
};
@@ -101,23 +108,6 @@ impl ProvenCompactCiphertextList {
CompactCiphertextListBuilder::new(pk)
}
pub fn verify_and_expand_with_key(
&self,
public_params: &CompactPkePublicParams,
pk: &CompactPublicKey,
sks: &crate::ServerKey,
) -> crate::Result<CompactCiphertextListExpander> {
// TODO check modes
self.0
.verify_and_expand(
public_params,
&pk.key.key,
IntegerCompactCiphertextListUnpackingMode::UnpackIfNecessary(sks.key.pbs_key()),
IntegerCompactCiphertextListCastingMode::NoCasting,
)
.map(|expander| CompactCiphertextListExpander { inner: expander })
}
pub fn verify_and_expand(
&self,
public_params: &CompactPkePublicParams,
@@ -146,7 +136,15 @@ impl ProvenCompactCiphertextList {
};
let casting_mode = if self.0.needs_casting() {
todo!("TODO check packing and casting modes")
IntegerCompactCiphertextListCastingMode::CastIfNecessary(
cpu_key.cpk_casting_key().ok_or_else(|| {
crate::Error::new(
"No casting key found in ServerKey, \
required to expand this CompactCiphertextList"
.to_string(),
)
})?,
)
} else {
IntegerCompactCiphertextListCastingMode::NoCasting
};
@@ -412,8 +410,6 @@ mod tests {
use super::*;
use crate::prelude::*;
#[cfg(feature = "zk-pok-experimental")]
use crate::shortint::parameters::test_parameters::PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS_TUNIFORM_2M64;
#[cfg(feature = "zk-pok-experimental")]
use crate::zk::CompactPkeCrs;
use crate::{set_server_key, FheInt64, FheUint16, FheUint2, FheUint32};
@@ -473,8 +469,10 @@ mod tests {
#[cfg(feature = "zk-pok-experimental")]
#[test]
fn test_proven_compact_list() {
use crate::shortint::parameters::classic::tuniform::p_fail_2_minus_64::ks_pbs::PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
let config = crate::ConfigBuilder::with_custom_parameters(
PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS_TUNIFORM_2M64,
PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64,
None,
)
.build();
@@ -529,4 +527,72 @@ mod tests {
assert!(expander.get::<FheUint16>(0).unwrap().is_err());
}
}
#[cfg(feature = "zk-pok-experimental")]
#[test]
fn test_proven_compact_list_with_casting() {
use crate::shortint::parameters::compact_public_key_only::PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
use crate::shortint::parameters::key_switching::PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
use crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
let config = crate::ConfigBuilder::with_custom_parameters(
PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64,
None,
)
.use_dedicated_compact_public_key_parameters((
PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64,
PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64,
))
.build();
let ck = crate::ClientKey::generate(config);
let pk = crate::CompactPublicKey::new(&ck);
let sks = crate::ServerKey::new(&ck);
set_server_key(sks);
// Intentionally low to that we test when multiple lists and proofs are needed
let crs = CompactPkeCrs::from_config(config, 32).unwrap();
let compact_list = ProvenCompactCiphertextList::builder(&pk)
.push(17u32)
.push(-1i64)
.push(false)
.push_with_num_bits(3u32, 2)
.unwrap()
.build_with_proof_packed(crs.public_params(), ZkComputeLoad::Proof)
.unwrap();
let serialized = bincode::serialize(&compact_list).unwrap();
let compact_list: ProvenCompactCiphertextList = bincode::deserialize(&serialized).unwrap();
let expander = compact_list
.verify_and_expand(crs.public_params(), &pk)
.unwrap();
{
let a: FheUint32 = expander.get(0).unwrap().unwrap();
let b: FheInt64 = expander.get(1).unwrap().unwrap();
let c: FheBool = expander.get(2).unwrap().unwrap();
let d: FheUint2 = expander.get(3).unwrap().unwrap();
let a: u32 = a.decrypt(&ck);
assert_eq!(a, 17);
let b: i64 = b.decrypt(&ck);
assert_eq!(b, -1);
let c = c.decrypt(&ck);
assert!(!c);
let d: u8 = d.decrypt(&ck);
assert_eq!(d, 3);
assert!(expander.get::<FheBool>(4).is_none());
}
{
// Incorrect type
assert!(expander.get::<FheInt64>(0).unwrap().is_err());
// Correct type but wrong number of bits
assert!(expander.get::<FheUint16>(0).unwrap().is_err());
}
}
}

View File

@@ -6,6 +6,15 @@ pub struct Config {
pub(crate) inner: IntegerConfig,
}
impl Config {
pub fn public_key_encryption_parameters(
&self,
) -> Result<crate::shortint::parameters::CompactPublicKeyEncryptionParameters, crate::Error>
{
self.inner.public_key_encryption_parameters()
}
}
/// The builder to create your config
///
/// The configuration is needed to select parameters you wish to use for these types
@@ -59,11 +68,23 @@ impl ConfigBuilder {
{
Self {
config: Config {
inner: IntegerConfig::new(block_parameters.into(), wopbs_block_parameters),
inner: IntegerConfig::new(block_parameters.into(), wopbs_block_parameters, None),
},
}
}
pub fn use_dedicated_compact_public_key_parameters(
mut self,
dedicated_compact_public_key_parameters: (
crate::shortint::parameters::CompactPublicKeyEncryptionParameters,
crate::shortint::parameters::ShortintKeySwitchingParameters,
),
) -> Self {
self.config.inner.dedicated_compact_public_key_parameters =
Some(dedicated_compact_public_key_parameters);
self
}
pub fn use_custom_parameters<P>(
mut self,
block_parameters: P,
@@ -72,7 +93,8 @@ impl ConfigBuilder {
where
P: Into<crate::shortint::PBSParameters>,
{
self.config.inner = IntegerConfig::new(block_parameters.into(), wopbs_block_parameters);
self.config.inner =
IntegerConfig::new(block_parameters.into(), wopbs_block_parameters, None);
self
}

View File

@@ -566,8 +566,7 @@ fn test_compact_public_key_big() {
fn test_compact_public_key_small() {
let config = ConfigBuilder::default()
.use_custom_parameters(
crate::shortint::parameters::classic::compact_pk
::PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS,
crate::shortint::parameters::classic::compact_pk::PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS,
None,
)
.build();

View File

@@ -4,11 +4,14 @@ use crate::high_level_api::{generate_keys, set_server_key, ConfigBuilder, FheUin
use crate::integer::U256;
use crate::safe_deserialization::safe_deserialize_conformant;
use crate::shortint::parameters::classic::compact_pk::*;
use crate::shortint::parameters::compact_public_key_only::PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
use crate::shortint::parameters::key_switching::PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
use crate::shortint::parameters::*;
use crate::{
ClientKey, CompactCiphertextList, CompactCiphertextListConformanceParams, CompactPublicKey,
CompressedFheUint16, CompressedFheUint256, CompressedFheUint32, CompressedPublicKey, FheInt16,
FheInt32, FheInt8, FheUint128, FheUint16, FheUint256, FheUint32, FheUint32ConformanceParams,
CompressedCompactPublicKey, CompressedFheUint16, CompressedFheUint256, CompressedFheUint32,
CompressedPublicKey, CompressedServerKey, FheInt16, FheInt32, FheInt8, FheUint128, FheUint16,
FheUint256, FheUint32, FheUint32ConformanceParams,
};
use rand::prelude::*;
@@ -473,3 +476,105 @@ fn test_safe_deserialize_conformant_compact_fhe_uint32() {
assert!(deserialized_a.is_conformant(&params));
}
#[test]
fn test_cpk_encrypt_cast_compute_hl() {
let param_pke_only = PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
let param_fhe = PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
let param_ksk = PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
let num_block = 4usize;
assert_eq!(param_pke_only.message_modulus, param_fhe.message_modulus);
assert_eq!(param_pke_only.carry_modulus, param_fhe.carry_modulus);
let modulus = param_fhe.message_modulus.0.pow(num_block as u32) as u64;
let (client_key, server_key) = generate_keys(
ConfigBuilder::with_custom_parameters(param_fhe, None)
.use_dedicated_compact_public_key_parameters((param_pke_only, param_ksk)),
);
set_server_key(server_key);
use rand::Rng;
let mut rng = rand::thread_rng();
let input_msg: u64 = rng.gen_range(0..modulus);
let pk = CompactPublicKey::new(&client_key);
// Encrypt a value and cast
let mut builder = CompactCiphertextList::builder(&pk);
let list = builder
.push_with_num_bits(input_msg, 8)
.unwrap()
.build_packed();
let expander = list.expand().unwrap();
let ct1_extracted_and_cast = expander.get::<FheUint8>(0).unwrap().unwrap();
let sanity_cast: u64 = ct1_extracted_and_cast.decrypt(&client_key);
assert_eq!(sanity_cast, input_msg);
let multiplier = rng.gen_range(0..modulus);
// Classical AP: DP, KS, PBS
let mul = &ct1_extracted_and_cast * multiplier as u8;
// High level decryption and test
let clear: u64 = mul.decrypt(&client_key);
assert_eq!(clear, (input_msg * multiplier) % modulus);
}
#[test]
fn test_compressed_cpk_encrypt_cast_compute_hl() {
let param_pke_only = PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
let param_fhe = PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
let param_ksk = PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
let num_block = 4usize;
assert_eq!(param_pke_only.message_modulus, param_fhe.message_modulus);
assert_eq!(param_pke_only.carry_modulus, param_fhe.carry_modulus);
let modulus = param_fhe.message_modulus.0.pow(num_block as u32) as u64;
let config = ConfigBuilder::with_custom_parameters(param_fhe, None)
.use_dedicated_compact_public_key_parameters((param_pke_only, param_ksk))
.build();
let client_key = ClientKey::generate(config);
let compressed_server_key = CompressedServerKey::new(&client_key);
let server_key = compressed_server_key.decompress();
set_server_key(server_key);
use rand::Rng;
let mut rng = rand::thread_rng();
let input_msg: u64 = rng.gen_range(0..modulus);
let compressed_pk = CompressedCompactPublicKey::new(&client_key);
let pk = compressed_pk.decompress();
// Encrypt a value and cast
let mut builder = CompactCiphertextList::builder(&pk);
let list = builder
.push_with_num_bits(input_msg, 8)
.unwrap()
.build_packed();
let expander = list.expand().unwrap();
let ct1_extracted_and_cast = expander.get::<FheUint8>(0).unwrap().unwrap();
let sanity_cast: u64 = ct1_extracted_and_cast.decrypt(&client_key);
assert_eq!(sanity_cast, input_msg);
let multiplier = rng.gen_range(0..modulus);
// Classical AP: DP, KS, PBS
let mul = &ct1_extracted_and_cast * multiplier as u8;
// High level decryption and test
let clear: u64 = mul.decrypt(&client_key);
assert_eq!(clear, (input_msg * multiplier) % modulus);
}

View File

@@ -4,7 +4,7 @@
use super::{CompressedServerKey, ServerKey};
use crate::high_level_api::config::Config;
use crate::high_level_api::keys::IntegerClientKey;
use crate::high_level_api::keys::{CompactPrivateKey, IntegerClientKey};
use crate::shortint::MessageModulus;
use concrete_csprng::seeders::Seed;
@@ -66,6 +66,7 @@ impl ClientKey {
) -> (
crate::integer::ClientKey,
Option<crate::shortint::WopbsParameters>,
Option<CompactPrivateKey>,
) {
self.key.into_raw_parts()
}
@@ -73,9 +74,17 @@ impl ClientKey {
pub fn from_raw_parts(
key: crate::integer::ClientKey,
wopbs_block_parameters: Option<crate::shortint::WopbsParameters>,
dedicated_compact_private_key: Option<(
crate::integer::CompactPrivateKey<Vec<u64>>,
crate::shortint::parameters::key_switching::ShortintKeySwitchingParameters,
)>,
) -> Self {
Self {
key: IntegerClientKey::from_raw_parts(key, wopbs_block_parameters),
key: IntegerClientKey::from_raw_parts(
key,
wopbs_block_parameters,
dedicated_compact_private_key,
),
}
}

View File

@@ -7,20 +7,31 @@ use crate::Error;
use concrete_csprng::seeders::Seed;
use serde::{Deserialize, Serialize};
// Clippy complained that fields end in _parameters, :roll_eyes:
#[allow(clippy::struct_field_names)]
#[derive(Copy, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub(crate) struct IntegerConfig {
pub(crate) block_parameters: crate::shortint::PBSParameters,
pub(crate) wopbs_block_parameters: Option<crate::shortint::WopbsParameters>,
pub(crate) dedicated_compact_public_key_parameters: Option<(
crate::shortint::parameters::CompactPublicKeyEncryptionParameters,
crate::shortint::parameters::ShortintKeySwitchingParameters,
)>,
}
impl IntegerConfig {
pub(crate) fn new(
block_parameters: crate::shortint::PBSParameters,
wopbs_block_parameters: Option<crate::shortint::WopbsParameters>,
dedicated_compact_public_key_parameters: Option<(
crate::shortint::parameters::CompactPublicKeyEncryptionParameters,
crate::shortint::parameters::ShortintKeySwitchingParameters,
)>,
) -> Self {
Self {
block_parameters,
wopbs_block_parameters,
dedicated_compact_public_key_parameters,
}
}
@@ -28,6 +39,7 @@ impl IntegerConfig {
Self {
block_parameters: crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS.into(),
wopbs_block_parameters: None,
dedicated_compact_public_key_parameters: None,
}
}
@@ -35,6 +47,7 @@ impl IntegerConfig {
Self {
block_parameters: crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_PBS_KS.into(),
wopbs_block_parameters: None,
dedicated_compact_public_key_parameters: None,
}
}
@@ -46,12 +59,29 @@ impl IntegerConfig {
self.wopbs_block_parameters = Some(wopbs_block_parameters);
}
pub fn public_key_encryption_parameters(
&self,
) -> Result<crate::shortint::parameters::CompactPublicKeyEncryptionParameters, crate::Error>
{
if let Some(p) = self.dedicated_compact_public_key_parameters {
Ok(p.0)
} else {
Ok(self.block_parameters.try_into()?)
}
}
}
pub(crate) type CompactPrivateKey = (
crate::integer::CompactPrivateKey<Vec<u64>>,
crate::shortint::parameters::key_switching::ShortintKeySwitchingParameters,
);
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub(crate) struct IntegerClientKey {
pub(crate) key: crate::integer::ClientKey,
pub(crate) wopbs_block_parameters: Option<crate::shortint::WopbsParameters>,
pub(crate) dedicated_compact_private_key: Option<CompactPrivateKey>,
}
impl IntegerClientKey {
@@ -64,9 +94,13 @@ impl IntegerClientKey {
let cks = crate::shortint::engine::ShortintEngine::new_from_seeder(&mut seeder)
.new_client_key(config.block_parameters.into());
let key = crate::integer::ClientKey::from(cks);
let dedicated_compact_private_key = config
.dedicated_compact_public_key_parameters
.map(|p| (crate::integer::CompactPrivateKey::new(p.0), p.1));
Self {
key,
wopbs_block_parameters: config.wopbs_block_parameters,
dedicated_compact_private_key,
}
}
@@ -76,12 +110,14 @@ impl IntegerClientKey {
) -> (
crate::integer::ClientKey,
Option<crate::shortint::WopbsParameters>,
Option<CompactPrivateKey>,
) {
let Self {
key,
wopbs_block_parameters,
dedicated_compact_private_key,
} = self;
(key, wopbs_block_parameters)
(key, wopbs_block_parameters, dedicated_compact_private_key)
}
/// Construct a, [`IntegerClientKey`] from its constituents.
@@ -92,6 +128,7 @@ impl IntegerClientKey {
pub fn from_raw_parts(
key: crate::integer::ClientKey,
wopbs_block_parameters: Option<crate::shortint::WopbsParameters>,
dedicated_compact_private_key: Option<CompactPrivateKey>,
) -> Self {
let shortint_cks: &crate::shortint::ClientKey = key.as_ref();
if let Some(wop_params) = wopbs_block_parameters.as_ref() {
@@ -105,9 +142,29 @@ impl IntegerClientKey {
);
}
if let Some(dedicated_compact_private_key) = dedicated_compact_private_key.as_ref() {
assert_eq!(
shortint_cks.parameters.message_modulus(),
dedicated_compact_private_key
.0
.key
.parameters()
.message_modulus,
);
assert_eq!(
shortint_cks.parameters.carry_modulus(),
dedicated_compact_private_key
.0
.key
.parameters()
.carry_modulus,
);
}
Self {
key,
wopbs_block_parameters,
dedicated_compact_private_key,
}
}
@@ -123,9 +180,13 @@ impl From<IntegerConfig> for IntegerClientKey {
"This API only supports parameters for which the MessageModulus is 2 or 4 (1 or 2 bits per block)",
);
let key = crate::integer::ClientKey::new(config.block_parameters);
let dedicated_compact_private_key = config
.dedicated_compact_public_key_parameters
.map(|p| (crate::integer::CompactPrivateKey::new(p.0), p.1));
Self {
key,
wopbs_block_parameters: config.wopbs_block_parameters,
dedicated_compact_private_key,
}
}
}
@@ -134,6 +195,12 @@ impl From<IntegerConfig> for IntegerClientKey {
pub struct IntegerServerKey {
pub(crate) key: crate::integer::ServerKey,
pub(crate) wopbs_key: Option<crate::integer::wopbs::WopbsKey>,
// Storing a KeySwitchingKeyView would require a self reference -> nightmare
// Storing a KeySwitchingKey would mean cloning the ServerKey and means more memory traffic to
// fetch the exact same key, so we store the part of the key that are not ServerKeys and we
// will create views when required
pub(crate) cpk_key_switching_key_material:
Option<crate::integer::key_switching_key::KeySwitchingKeyMaterial>,
}
impl IntegerServerKey {
@@ -146,9 +213,24 @@ impl IntegerServerKey {
.map(|wopbs_params| {
crate::integer::wopbs::WopbsKey::new_wopbs_key(cks, &base_integer_key, wopbs_params)
});
let cpk_key_switching_key_material =
client_key
.dedicated_compact_private_key
.as_ref()
.map(|(private_key, ksk_params)| {
let build_helper =
crate::integer::key_switching_key::KeySwitchingKeyBuildHelper::new(
(private_key, None),
(cks, &base_integer_key),
*ksk_params,
);
build_helper.into()
});
Self {
key: base_integer_key,
wopbs_key,
cpk_key_switching_key_material,
}
}
@@ -156,6 +238,18 @@ impl IntegerServerKey {
&self.key
}
pub(in crate::high_level_api) fn cpk_casting_key(
&self,
) -> Option<crate::integer::key_switching_key::KeySwitchingKeyView> {
self.cpk_key_switching_key_material.as_ref().map(|k| {
crate::integer::key_switching_key::KeySwitchingKeyView::from_keyswitching_key_material(
k.as_view(),
self.pbs_key(),
None,
)
})
}
pub(in crate::high_level_api) fn message_modulus(&self) -> MessageModulus {
self.key.message_modulus()
}
@@ -164,11 +258,13 @@ impl IntegerServerKey {
#[derive(Clone, serde::Serialize, serde::Deserialize)]
pub struct IntegerCompressedServerKey {
pub(crate) key: crate::integer::CompressedServerKey,
pub(crate) cpk_key_switching_key_material:
Option<crate::integer::key_switching_key::CompressedKeySwitchingKeyMaterial>,
}
impl IntegerCompressedServerKey {
pub(in crate::high_level_api) fn new(client_key: &IntegerClientKey) -> Self {
let integer_key = &client_key.key;
let cks = &client_key.key;
assert!(
client_key.wopbs_block_parameters.is_none(),
"The configuration used to create the ClientKey \
@@ -178,22 +274,57 @@ impl IntegerCompressedServerKey {
to create a CompressedServerKey.
"
);
let key = crate::integer::CompressedServerKey::new_radix_compressed_server_key(integer_key);
Self { key }
let key = crate::integer::CompressedServerKey::new_radix_compressed_server_key(cks);
let cpk_key_switching_key_material =
client_key
.dedicated_compact_private_key
.as_ref()
.map(|(private_key, ksk_params)| {
let build_helper =
crate::integer::key_switching_key::CompressedKeySwitchingKeyBuildHelper::new(
(private_key, None),
(cks, &key),
*ksk_params,
);
build_helper.into()
});
Self {
key,
cpk_key_switching_key_material,
}
}
pub fn into_raw_parts(self) -> crate::integer::CompressedServerKey {
self.key
pub fn into_raw_parts(
self,
) -> (
crate::integer::CompressedServerKey,
Option<crate::integer::key_switching_key::CompressedKeySwitchingKeyMaterial>,
) {
(self.key, self.cpk_key_switching_key_material)
}
pub fn from_raw_parts(key: crate::integer::CompressedServerKey) -> Self {
Self { key }
pub fn from_raw_parts(
key: crate::integer::CompressedServerKey,
cpk_key_switching_key_material: Option<
crate::integer::key_switching_key::CompressedKeySwitchingKeyMaterial,
>,
) -> Self {
Self {
key,
cpk_key_switching_key_material,
}
}
pub(in crate::high_level_api) fn decompress(&self) -> IntegerServerKey {
IntegerServerKey {
key: self.key.decompress(),
wopbs_key: None,
cpk_key_switching_key_material: self.cpk_key_switching_key_material.as_ref().map(
crate::integer::key_switching_key::CompressedKeySwitchingKeyMaterial::decompress,
),
}
}
}
@@ -209,9 +340,10 @@ impl IntegerCompactPublicKey {
}
pub(in crate::high_level_api) fn try_new(client_key: &IntegerClientKey) -> Result<Self, Error> {
let cks = &client_key.key;
let key = CompactPublicKey::try_new(cks)?;
let key = match &client_key.dedicated_compact_private_key {
Some(compact_private_key) => CompactPublicKey::try_new(&compact_private_key.0)?,
None => CompactPublicKey::try_new(&client_key.key)?,
};
Ok(Self { key })
}
@@ -232,11 +364,18 @@ pub(in crate::high_level_api) struct IntegerCompressedCompactPublicKey {
impl IntegerCompressedCompactPublicKey {
pub(in crate::high_level_api) fn new(client_key: &IntegerClientKey) -> Self {
let cks = &client_key.key;
Self::try_new(client_key).expect("Incompatible parameters")
}
let key = CompressedCompactPublicKey::new(cks);
pub(in crate::high_level_api) fn try_new(client_key: &IntegerClientKey) -> Result<Self, Error> {
let key = match &client_key.dedicated_compact_private_key {
Some(compact_private_key) => {
CompressedCompactPublicKey::try_new(&compact_private_key.0)?
}
None => CompressedCompactPublicKey::try_new(&client_key.key)?,
};
Self { key }
Ok(Self { key })
}
/// Deconstruct a [`IntegerCompressedCompactPublicKey`] into its constituents.

View File

@@ -7,6 +7,7 @@ mod key_switching_key;
use crate::high_level_api::config::Config;
pub use client::ClientKey;
pub(crate) use inner::CompactPrivateKey;
pub use key_switching_key::KeySwitchingKey;
pub use public::{CompactPublicKey, CompressedCompactPublicKey, CompressedPublicKey, PublicKey};
#[cfg(feature = "gpu")]

View File

@@ -32,18 +32,30 @@ impl ServerKey {
) -> (
crate::integer::ServerKey,
Option<crate::integer::wopbs::WopbsKey>,
Option<crate::integer::key_switching_key::KeySwitchingKeyMaterial>,
) {
let IntegerServerKey { key, wopbs_key } = (*self.key).clone();
let IntegerServerKey {
key,
wopbs_key,
cpk_key_switching_key_material,
} = (*self.key).clone();
(key, wopbs_key)
(key, wopbs_key, cpk_key_switching_key_material)
}
pub fn from_raw_parts(
key: crate::integer::ServerKey,
wopbs_key: Option<crate::integer::wopbs::WopbsKey>,
cpk_key_switching_key_material: Option<
crate::integer::key_switching_key::KeySwitchingKeyMaterial,
>,
) -> Self {
Self {
key: Arc::new(IntegerServerKey { key, wopbs_key }),
key: Arc::new(IntegerServerKey {
key,
wopbs_key,
cpk_key_switching_key_material,
}),
}
}
}
@@ -120,13 +132,26 @@ impl CompressedServerKey {
}
}
pub fn into_raw_parts(self) -> crate::integer::CompressedServerKey {
pub fn into_raw_parts(
self,
) -> (
crate::integer::CompressedServerKey,
Option<crate::integer::key_switching_key::CompressedKeySwitchingKeyMaterial>,
) {
self.integer_key.into_raw_parts()
}
pub fn from_raw_parts(integer_key: crate::integer::CompressedServerKey) -> Self {
pub fn from_raw_parts(
integer_key: crate::integer::CompressedServerKey,
cpk_key_switching_key_material: Option<
crate::integer::key_switching_key::CompressedKeySwitchingKeyMaterial,
>,
) -> Self {
Self {
integer_key: IntegerCompressedServerKey::from_raw_parts(integer_key),
integer_key: IntegerCompressedServerKey::from_raw_parts(
integer_key,
cpk_key_switching_key_material,
),
}
}

View File

@@ -8,20 +8,23 @@ impl CompactPkeCrs {
///
/// This function assumes that packing will be applied during ZK proof.
pub fn from_config(config: Config, max_bit_size: usize) -> crate::Result<Self> {
let parameters = config.inner.block_parameters;
let compact_encryption_parameters = config.public_key_encryption_parameters()?;
if parameters.carry_modulus().0 < parameters.message_modulus().0 {
if compact_encryption_parameters.carry_modulus.0
< compact_encryption_parameters.message_modulus.0
{
return Err(Error::new(
"In order to build a packed compact ciphertext list, \
"In order to build a ZK-CRS for packed compact ciphertext list encryption, \
parameters must have CarryModulus >= MessageModulus"
.to_string(),
));
}
let carry_and_message_bit_capacity =
(parameters.carry_modulus().0 * parameters.message_modulus().0).ilog2() as usize;
let carry_and_message_bit_capacity = (compact_encryption_parameters.carry_modulus.0
* compact_encryption_parameters.message_modulus.0)
.ilog2() as usize;
let max_num_message = max_bit_size.div_ceil(carry_and_message_bit_capacity);
let crs = Self::from_shortint_params(config.inner.block_parameters, max_num_message)?;
let crs = Self::from_shortint_params(compact_encryption_parameters, max_num_message)?;
Ok(crs)
}
}

View File

@@ -454,11 +454,17 @@ impl CompactCiphertextList {
let expanded_blocks = if is_packed {
match unpacking_mode {
IntegerCompactCiphertextListUnpackingMode::UnpackIfNecessary(sks) => {
if !self.is_compatible_with_unpacking_server_key(sks) {
return Err(crate::Error::new(
"This compact list is not conformant with the given server key"
.to_string(),
));
let degree = self.ct_list.degree;
let mut conformance_params = sks.key.conformance_params();
conformance_params.degree = degree;
for ct in expanded_blocks.iter() {
if !ct.is_conformant(&conformance_params) {
return Err(crate::Error::new(
"This compact list is not conformant with the given server key"
.to_string(),
));
}
}
extract_message_and_carries(expanded_blocks, sks)
@@ -483,18 +489,12 @@ impl CompactCiphertextList {
self.ct_list.size_bytes()
}
pub fn is_compatible_with_unpacking_server_key(&self, sks: &ServerKey) -> bool {
let mut conformance_params = sks.key.conformance_params();
conformance_params.degree = self.ct_list.degree;
self.is_conformant_with_shortint_params(conformance_params)
}
fn is_conformant_with_shortint_params(
&self,
shortint_params: CiphertextConformanceParams,
) -> bool {
let mut num_blocks: usize = self.info.iter().copied().map(DataKind::num_blocks).sum();
// This expects packing, halve the number of blocks with enough capacity
if shortint_params.degree.get()
== (shortint_params.message_modulus.0 * shortint_params.carry_modulus.0) - 1
{
@@ -593,27 +593,36 @@ impl ProvenCompactCiphertextList {
#[cfg(test)]
mod tests {
use crate::integer::ciphertext::CompactCiphertextList;
use crate::integer::key_switching_key::KeySwitchingKey;
use crate::integer::parameters::{
IntegerCompactCiphertextListCastingMode, IntegerCompactCiphertextListUnpackingMode,
};
use crate::integer::{ClientKey, CompactPublicKey, RadixCiphertext, ServerKey};
use crate::shortint::parameters::test_parameters::PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS_TUNIFORM_2M64;
use crate::integer::{
ClientKey, CompactPrivateKey, CompactPublicKey, RadixCiphertext, ServerKey,
};
use crate::shortint::parameters::classic::tuniform::p_fail_2_minus_64::ks_pbs::PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
use crate::shortint::parameters::compact_public_key_only::PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
use crate::shortint::parameters::key_switching::PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
use crate::zk::{CompactPkeCrs, ZkComputeLoad};
use rand::random;
#[test]
fn test_zk_compact_ciphertext_list_encryption_ci_run_filter() {
let params = PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS_TUNIFORM_2M64;
let pke_params = PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
let ksk_params = PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
let fhe_params = PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
let num_blocks = 4usize;
let modulus = (params.message_modulus.0 as u64)
let modulus = (pke_params.message_modulus.0 as u64)
.checked_pow(num_blocks as u32)
.unwrap();
let crs = CompactPkeCrs::from_shortint_params(params, 512).unwrap();
let cks = ClientKey::new(params);
let crs = CompactPkeCrs::from_shortint_params(pke_params, 512).unwrap();
let cks = ClientKey::new(fhe_params);
let sk = ServerKey::new_radix_server_key(&cks);
let pk = CompactPublicKey::new(&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 msgs = (0..512)
.map(|_| random::<u64>() % modulus)
@@ -629,7 +638,7 @@ mod tests {
crs.public_params(),
&pk,
IntegerCompactCiphertextListUnpackingMode::UnpackIfNecessary(&sk),
IntegerCompactCiphertextListCastingMode::NoCasting,
IntegerCompactCiphertextListCastingMode::CastIfNecessary(ksk.as_view()),
)
.unwrap();

View File

@@ -1,4 +1,4 @@
use super::{ClientKey, ServerKey};
use super::{ClientKey, CompressedServerKey, ServerKey};
use crate::integer::client_key::secret_encryption_key::SecretEncryptionKeyView;
use crate::integer::IntegerCiphertext;
use crate::shortint::parameters::ShortintKeySwitchingParameters;
@@ -8,11 +8,73 @@ use serde::{Deserialize, Serialize};
#[cfg(test)]
mod test;
// This is used to have the ability to build a keyswitching key without owning the ServerKey
// It is a bit of a hack, but at this point it seems ok
pub(crate) struct KeySwitchingKeyBuildHelper<'keys> {
pub(crate) build_helper: crate::shortint::key_switching_key::KeySwitchingKeyBuildHelper<'keys>,
}
impl<'keys> KeySwitchingKeyBuildHelper<'keys> {
pub fn new<'input_key, InputEncryptionKey>(
input_key_pair: (InputEncryptionKey, Option<&'keys ServerKey>),
output_key_pair: (&'keys ClientKey, &'keys ServerKey),
params: ShortintKeySwitchingParameters,
) -> Self
where
InputEncryptionKey: Into<SecretEncryptionKeyView<'input_key>>,
{
let (secret_key, src_sks) = input_key_pair;
let secret_key: SecretEncryptionKeyView<'_> = secret_key.into();
Self {
build_helper: crate::shortint::key_switching_key::KeySwitchingKeyBuildHelper::new(
(&secret_key.key, src_sks.map(AsRef::as_ref)),
(output_key_pair.0.as_ref(), output_key_pair.1.as_ref()),
params,
),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct KeySwitchingKeyMaterial {
pub(crate) material: crate::shortint::key_switching_key::KeySwitchingKeyMaterial,
}
impl KeySwitchingKeyMaterial {
pub fn as_view(&self) -> KeySwitchingKeyMaterialView<'_> {
KeySwitchingKeyMaterialView {
material: self.material.as_view(),
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct KeySwitchingKey {
pub(crate) key: crate::shortint::KeySwitchingKey,
}
impl<'keys> From<KeySwitchingKeyBuildHelper<'keys>> for KeySwitchingKey {
fn from(value: KeySwitchingKeyBuildHelper) -> Self {
Self {
key: value.build_helper.into(),
}
}
}
impl<'keys> From<KeySwitchingKeyBuildHelper<'keys>> for KeySwitchingKeyMaterial {
fn from(value: KeySwitchingKeyBuildHelper) -> Self {
Self {
material: value.build_helper.key_switching_key_material,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct KeySwitchingKeyMaterialView<'key> {
pub(crate) material: crate::shortint::key_switching_key::KeySwitchingKeyMaterialView<'key>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct KeySwitchingKeyView<'keys> {
pub(crate) key: crate::shortint::KeySwitchingKeyView<'keys>,
@@ -40,7 +102,11 @@ impl KeySwitchingKey {
),
};
assert!(ret.key.cast_rshift == 0, "Attempt to build a KeySwitchingKey between integer key pairs with different message modulus and carry");
assert!(
ret.key.key_switching_key_material.cast_rshift == 0,
"Attempt to build a KeySwitchingKey \
between integer key pairs with different message modulus and carry"
);
ret
}
@@ -78,3 +144,123 @@ impl KeySwitchingKey {
}
}
}
impl<'keys> KeySwitchingKeyView<'keys> {
pub fn from_keyswitching_key_material(
key_switching_key_material: KeySwitchingKeyMaterialView<'keys>,
dest_server_key: &'keys ServerKey,
src_server_key: Option<&'keys ServerKey>,
) -> Self {
Self {
key: crate::shortint::KeySwitchingKeyView::from_raw_parts(
key_switching_key_material.material,
dest_server_key.as_ref(),
src_server_key.map(AsRef::as_ref),
),
}
}
}
// This is used to have the ability to build a keyswitching key without owning the ServerKey
// It is a bit of a hack, but at this point it seems ok
pub(crate) struct CompressedKeySwitchingKeyBuildHelper<'keys> {
pub(crate) build_helper:
crate::shortint::key_switching_key::CompressedKeySwitchingKeyBuildHelper<'keys>,
}
impl<'keys> CompressedKeySwitchingKeyBuildHelper<'keys> {
pub fn new<'input_key, InputEncryptionKey>(
input_key_pair: (InputEncryptionKey, Option<&'keys CompressedServerKey>),
output_key_pair: (&'keys ClientKey, &'keys CompressedServerKey),
params: ShortintKeySwitchingParameters,
) -> Self
where
InputEncryptionKey: Into<SecretEncryptionKeyView<'input_key>>,
{
let (secret_key, src_sks) = input_key_pair;
let secret_key: SecretEncryptionKeyView<'_> = secret_key.into();
Self {
build_helper:
crate::shortint::key_switching_key::CompressedKeySwitchingKeyBuildHelper::new(
(&secret_key.key, src_sks.map(|k| &k.key)),
(output_key_pair.0.as_ref(), &output_key_pair.1.key),
params,
),
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CompressedKeySwitchingKeyMaterial {
pub(crate) material: crate::shortint::key_switching_key::CompressedKeySwitchingKeyMaterial,
}
impl CompressedKeySwitchingKeyMaterial {
pub fn decompress(&self) -> KeySwitchingKeyMaterial {
KeySwitchingKeyMaterial {
material: self.material.decompress(),
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CompressedKeySwitchingKey {
pub(crate) key: crate::shortint::CompressedKeySwitchingKey,
}
impl<'keys> From<CompressedKeySwitchingKeyBuildHelper<'keys>> for CompressedKeySwitchingKey {
fn from(value: CompressedKeySwitchingKeyBuildHelper) -> Self {
Self {
key: value.build_helper.into(),
}
}
}
impl<'keys> From<CompressedKeySwitchingKeyBuildHelper<'keys>>
for CompressedKeySwitchingKeyMaterial
{
fn from(value: CompressedKeySwitchingKeyBuildHelper) -> Self {
Self {
material: value.build_helper.key_switching_key_material,
}
}
}
impl CompressedKeySwitchingKey {
pub fn new<'input_key, InputEncryptionKey, ClientKeyType>(
input_key_pair: (InputEncryptionKey, Option<&CompressedServerKey>),
output_key_pair: (&ClientKeyType, &CompressedServerKey),
params: ShortintKeySwitchingParameters,
) -> Self
where
InputEncryptionKey: Into<SecretEncryptionKeyView<'input_key>>,
ClientKeyType: AsRef<ClientKey>,
{
let input_secret_encryption_key: SecretEncryptionKeyView<'_> = input_key_pair.0.into();
let ret = Self {
key: crate::shortint::CompressedKeySwitchingKey::new(
(
input_secret_encryption_key.key,
input_key_pair.1.map(|k| &k.key),
),
(&output_key_pair.0.as_ref().key, &output_key_pair.1.key),
params,
),
};
assert!(
ret.key.key_switching_key_material.cast_rshift == 0,
"Attempt to build a CompressedKeySwitchingKey \
between integer key pairs with different message modulus and carry"
);
ret
}
pub fn decompress(&self) -> KeySwitchingKey {
KeySwitchingKey {
key: self.key.decompress(),
}
}
}

View File

@@ -7,13 +7,10 @@ use crate::integer::{
ClientKey, CompactPrivateKey, CompactPublicKey, CrtClientKey, IntegerCiphertext,
IntegerKeyKind, RadixCiphertext, RadixClientKey, ServerKey,
};
use crate::shortint::parameters::compact_public_key_only::{
CompactCiphertextListExpansionKind, CompactPublicKeyEncryptionParameters,
};
use crate::shortint::parameters::compact_public_key_only::PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
use crate::shortint::parameters::key_switching::PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
use crate::shortint::parameters::{
CarryModulus, CiphertextModulus, ClassicPBSParameters, DecompositionBaseLog,
DecompositionLevelCount, DynamicDistribution, EncryptionKeyChoice, GlweDimension, LweDimension,
MaxNoiseLevel, MessageModulus, PolynomialSize, ShortintKeySwitchingParameters,
ShortintKeySwitchingParameters, PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64,
};
use crate::shortint::prelude::{PARAM_MESSAGE_1_CARRY_1_KS_PBS, PARAM_MESSAGE_2_CARRY_2_KS_PBS};
@@ -165,44 +162,9 @@ fn gen_multi_keys_test_integer_to_integer_ci_run_filter() {
#[test]
fn test_cpk_encrypt_cast_compute_ci_run_filter() {
pub const PARAM_PKE_MESSAGE_2_CARRY_2_132_64: CompactPublicKeyEncryptionParameters =
CompactPublicKeyEncryptionParameters {
encryption_lwe_dimension: LweDimension(1024),
encryption_noise_distribution: DynamicDistribution::new_t_uniform(42),
message_modulus: MessageModulus(4),
carry_modulus: CarryModulus(4),
ciphertext_modulus: CiphertextModulus::new_native(),
expansion_kind: CompactCiphertextListExpansionKind::RequiresCasting,
};
pub const PARAM_FHE_MESSAGE_2_CARRY_2_132_64: ClassicPBSParameters = ClassicPBSParameters {
lwe_dimension: LweDimension(887),
glwe_dimension: GlweDimension(1),
polynomial_size: PolynomialSize(2048),
lwe_noise_distribution: DynamicDistribution::new_t_uniform(45),
glwe_noise_distribution: DynamicDistribution::new_t_uniform(15),
pbs_base_log: DecompositionBaseLog(22),
pbs_level: DecompositionLevelCount(1),
ks_base_log: DecompositionBaseLog(3),
ks_level: DecompositionLevelCount(5),
message_modulus: MessageModulus(4),
carry_modulus: CarryModulus(4),
max_noise_level: MaxNoiseLevel::new(5),
log2_p_fail: -64.0,
ciphertext_modulus: CiphertextModulus::new_native(),
encryption_key_choice: EncryptionKeyChoice::Big,
};
pub const PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_132_64: ShortintKeySwitchingParameters =
ShortintKeySwitchingParameters {
ks_level: DecompositionLevelCount(5),
ks_base_log: DecompositionBaseLog(3),
destination_key: EncryptionKeyChoice::Big,
};
let param_pke_only = PARAM_PKE_MESSAGE_2_CARRY_2_132_64;
let param_fhe = PARAM_FHE_MESSAGE_2_CARRY_2_132_64;
let param_ksk = PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_132_64;
let param_pke_only = PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
let param_fhe = PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
let param_ksk = PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
let num_block = 4usize;

View File

@@ -38,15 +38,13 @@ impl CompactPrivateKey<Vec<u64>> {
}
}
impl<'key, C: Container<Element = u64>> TryFrom<&'key CompactPrivateKey<C>>
impl<'key, C: Container<Element = u64>> From<&'key CompactPrivateKey<C>>
for CompactPrivateKey<&'key [u64]>
{
type Error = crate::Error;
fn try_from(value: &'key CompactPrivateKey<C>) -> Result<Self, Self::Error> {
Ok(Self {
fn from(value: &'key CompactPrivateKey<C>) -> Self {
Self {
key: value.key().as_view(),
})
}
}
}
@@ -79,22 +77,28 @@ pub struct CompactPublicKey {
}
impl CompactPublicKey {
pub fn new<'data, C>(compact_private_key: C) -> Self
pub fn new<'data, C, E>(compact_private_key: C) -> Self
where
C: TryInto<CompactPrivateKey<&'data [u64]>, Error = crate::Error>,
C: TryInto<CompactPrivateKey<&'data [u64]>, Error = E>,
crate::Error: From<E>,
{
Self::try_new(compact_private_key).expect(
"Incompatible parameters, the lwe_dimension of the secret key must be a power of two",
)
}
pub fn try_new<'data, C>(input_key: C) -> Result<Self, crate::Error>
pub fn try_new<'data, C, E>(input_key: C) -> Result<Self, crate::Error>
where
C: TryInto<CompactPrivateKey<&'data [u64]>, Error = crate::Error>,
C: TryInto<CompactPrivateKey<&'data [u64]>, Error = E>,
crate::Error: From<E>,
{
let compact_private_key: CompactPrivateKey<&[u64]> = input_key.try_into()?;
let key = ShortintCompactPublicKey::new(&compact_private_key.key);
// Trait solver is having a hard time understanding what's happening (it sees we have an
// Infallible error type but still complains), give a nudge
let key = ShortintCompactPublicKey::new::<'_, _, std::convert::Infallible>(
&compact_private_key.key,
);
Ok(Self { key })
}
@@ -155,9 +159,25 @@ pub struct CompressedCompactPublicKey {
}
impl CompressedCompactPublicKey {
pub fn new(client_key: &ClientKey) -> Self {
let key = ShortintCompressedCompactPublicKey::new(&client_key.key);
Self { key }
pub fn new<'data, C, E>(compact_private_key: C) -> Self
where
C: TryInto<CompactPrivateKey<&'data [u64]>, Error = E>,
crate::Error: From<E>,
{
Self::try_new(compact_private_key).expect(
"Incompatible parameters, the lwe_dimension of the secret key must be a power of two",
)
}
pub fn try_new<'data, C, E>(input_key: C) -> Result<Self, crate::Error>
where
C: TryInto<CompactPrivateKey<&'data [u64]>, Error = E>,
crate::Error: From<E>,
{
let compact_private_key: CompactPrivateKey<&[u64]> = input_key.try_into()?;
let key = ShortintCompressedCompactPublicKey::new(&compact_private_key.key);
Ok(Self { key })
}
/// Deconstruct a [`CompressedCompactPublicKey`] into its constituents.

View File

@@ -32,6 +32,17 @@ impl TfheConfigBuilder {
Self(self.0.use_custom_parameters(block_parameters.0, None))
}
#[wasm_bindgen]
pub fn use_dedicated_compact_public_key_parameters(
self,
compact_public_key_parameters: &crate::js_on_wasm_api::shortint::ShortintCompactPublicKeyEncryptionParameters,
) -> Self {
Self(self.0.use_dedicated_compact_public_key_parameters((
compact_public_key_parameters.compact_pke_params,
compact_public_key_parameters.casting_parameters,
)))
}
#[wasm_bindgen]
pub fn build(self) -> TfheConfig {
TfheConfig(self.0.build())

View File

@@ -2,6 +2,8 @@ use crate::core_crypto::commons::generators::DeterministicSeeder;
use crate::core_crypto::commons::math::random::Seed;
use crate::core_crypto::prelude::ActivatedRandomGenerator;
use crate::shortint::parameters::classic::compact_pk::*;
use crate::shortint::parameters::compact_public_key_only::PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
use crate::shortint::parameters::key_switching::PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
use crate::shortint::parameters::*;
use std::panic::set_hook;
use wasm_bindgen::prelude::*;
@@ -30,6 +32,14 @@ pub struct Shortint {}
#[wasm_bindgen]
pub struct ShortintParameters(pub(crate) crate::shortint::ClassicPBSParameters);
#[wasm_bindgen]
pub struct ShortintCompactPublicKeyEncryptionParameters {
pub(crate) compact_pke_params:
crate::shortint::parameters::compact_public_key_only::CompactPublicKeyEncryptionParameters,
pub(crate) casting_parameters:
crate::shortint::parameters::key_switching::ShortintKeySwitchingParameters,
}
#[wasm_bindgen]
impl ShortintParameters {
#[wasm_bindgen]
@@ -182,6 +192,28 @@ pub struct ShortintNoiseDistribution(
pub(crate) crate::core_crypto::commons::math::random::DynamicDistribution<u64>,
);
// TODO: use macros once we have more parameters using the same pattern as
// expose_predefined_parameters
#[wasm_bindgen]
#[allow(non_camel_case_types)]
pub enum ShortintCompactPublicKeyEncryptionParametersName {
SHORTINT_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64,
}
#[wasm_bindgen]
impl ShortintCompactPublicKeyEncryptionParameters {
#[allow(clippy::needless_pass_by_value)]
#[wasm_bindgen(constructor)]
pub fn new(name: ShortintCompactPublicKeyEncryptionParametersName) -> Self {
match name {
ShortintCompactPublicKeyEncryptionParametersName::SHORTINT_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64 => Self {
compact_pke_params: PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64,
casting_parameters: PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64,
}
}
}
}
macro_rules! expose_predefined_parameters {
(
$(

View File

@@ -5,6 +5,7 @@ use super::standard::Ciphertext;
use crate::conformance::ParameterSetConformant;
use crate::core_crypto::commons::traits::ContiguousEntityContainer;
use crate::core_crypto::entities::*;
use crate::shortint::parameters::compact_public_key_only::CompactCiphertextListCastingMode;
pub use crate::shortint::parameters::ShortintCompactCiphertextListCastingMode;
use crate::shortint::parameters::{
CarryModulus, CompactCiphertextListExpansionKind, MessageModulus,
@@ -56,20 +57,6 @@ impl CompactCiphertextList {
&self,
casting_mode: ShortintCompactCiphertextListCastingMode<'_>,
) -> Result<Vec<Ciphertext>, crate::Error> {
if matches!(
self.expansion_kind,
CompactCiphertextListExpansionKind::RequiresCasting
) && matches!(
casting_mode,
ShortintCompactCiphertextListCastingMode::NoCasting
) {
return Err(crate::Error::new(String::from(
"Cannot expand a CompactCiphertextList that requires casting without casting, \
please provide a shortint::KeySwitchingKey passing it with the enum variant \
CompactCiphertextListExpansionMode::CastIfNecessary as casting_mode.",
)));
}
let mut output_lwe_ciphertext_list = LweCiphertextList::new(
0u64,
self.ct_list.lwe_size(),
@@ -91,35 +78,43 @@ impl CompactCiphertextList {
par_expand_lwe_compact_ciphertext_list(&mut output_lwe_ciphertext_list, &self.ct_list);
}
match self.expansion_kind {
CompactCiphertextListExpansionKind::RequiresCasting => match casting_mode {
ShortintCompactCiphertextListCastingMode::CastIfNecessary(casting_key) => {
let pbs_order = casting_key.dest_server_key.pbs_order;
match (self.expansion_kind, casting_mode) {
(
CompactCiphertextListExpansionKind::RequiresCasting,
CompactCiphertextListCastingMode::NoCasting,
) => Err(crate::Error::new(String::from(
"Cannot expand a CompactCiphertextList that requires casting without casting, \
please provide a shortint::KeySwitchingKey passing it with the enum variant \
CompactCiphertextListExpansionMode::CastIfNecessary as casting_mode.",
))),
(
CompactCiphertextListExpansionKind::RequiresCasting,
CompactCiphertextListCastingMode::CastIfNecessary(casting_key),
) => {
let pbs_order = casting_key.dest_server_key.pbs_order;
let res = output_lwe_ciphertext_list
.iter()
.map(|lwe_view| {
let lwe_to_cast = LweCiphertext::from_container(
lwe_view.as_ref().to_vec(),
self.ct_list.ciphertext_modulus(),
);
let shortint_ct_to_cast = Ciphertext {
ct: lwe_to_cast,
degree: self.degree,
message_modulus: self.message_modulus,
carry_modulus: self.carry_modulus,
pbs_order,
noise_level: self.noise_level,
};
let res = output_lwe_ciphertext_list
.iter()
.map(|lwe_view| {
let lwe_to_cast = LweCiphertext::from_container(
lwe_view.as_ref().to_vec(),
self.ct_list.ciphertext_modulus(),
);
let shortint_ct_to_cast = Ciphertext {
ct: lwe_to_cast,
degree: self.degree,
message_modulus: self.message_modulus,
carry_modulus: self.carry_modulus,
pbs_order,
noise_level: self.noise_level,
};
casting_key.cast(&shortint_ct_to_cast)
})
.collect::<Vec<_>>();
Ok(res)
}
ShortintCompactCiphertextListCastingMode::NoCasting => unreachable!(),
},
CompactCiphertextListExpansionKind::NoCasting(pbs_order) => {
casting_key.cast(&shortint_ct_to_cast)
})
.collect::<Vec<_>>();
Ok(res)
}
(CompactCiphertextListExpansionKind::NoCasting(pbs_order), _) => {
let res = output_lwe_ciphertext_list
.iter()
.map(|lwe_view| {

View File

@@ -1,8 +1,10 @@
use crate::core_crypto::algorithms::verify_lwe_compact_ciphertext_list;
use crate::core_crypto::prelude::verify_lwe_ciphertext;
use crate::shortint::ciphertext::CompactCiphertextList;
use crate::shortint::parameters::ShortintCompactCiphertextListCastingMode;
use crate::shortint::{Ciphertext, CompactPublicKey, EncryptionKeyChoice};
use crate::shortint::parameters::{
CompactPublicKeyEncryptionParameters, ShortintCompactCiphertextListCastingMode,
};
use crate::shortint::{Ciphertext, CompactPublicKey};
use crate::zk::{CompactPkeCrs, CompactPkeProof, CompactPkePublicParams, ZkVerificationOutCome};
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
@@ -11,22 +13,18 @@ impl CompactPkeCrs {
/// Construct the CRS that corresponds to the given parameters
///
/// max_num_message is how many message a single proof can prove
pub fn from_shortint_params(
params: impl Into<crate::shortint::PBSParameters>,
max_num_message: usize,
) -> crate::Result<Self> {
let params = params.into();
let (size, noise_distribution) = match params.encryption_key_choice() {
EncryptionKeyChoice::Big => {
let size = params
.glwe_dimension()
.to_equivalent_lwe_dimension(params.polynomial_size());
(size, params.glwe_noise_distribution())
}
EncryptionKeyChoice::Small => (params.lwe_dimension(), params.lwe_noise_distribution()),
};
pub fn from_shortint_params<P, E>(params: P, max_num_message: usize) -> crate::Result<Self>
where
P: TryInto<CompactPublicKeyEncryptionParameters, Error = E>,
crate::Error: From<E>,
{
let params: CompactPublicKeyEncryptionParameters = params.try_into()?;
let (size, noise_distribution) = (
params.encryption_lwe_dimension,
params.encryption_noise_distribution,
);
let mut plaintext_modulus = (params.message_modulus().0 * params.carry_modulus().0) as u64;
let mut plaintext_modulus = (params.message_modulus.0 * params.carry_modulus.0) as u64;
// Our plaintext modulus does not take into account the bit of padding
plaintext_modulus *= 2;
@@ -35,7 +33,7 @@ impl CompactPkeCrs {
size,
max_num_message,
noise_distribution,
params.ciphertext_modulus(),
params.ciphertext_modulus,
plaintext_modulus,
&mut engine.random_generator,
)

View File

@@ -184,6 +184,35 @@ impl ShortintEngine {
)
}
pub(crate) fn new_seeded_key_switching_key(
&mut self,
input_secret_key: &SecretEncryptionKeyView<'_>,
output_client_key: &ClientKey,
params: ShortintKeySwitchingParameters,
) -> SeededLweKeyswitchKeyOwned<u64> {
let (output_secret_key, encryption_noise) = match params.destination_key {
EncryptionKeyChoice::Big => (
output_client_key.large_lwe_secret_key(),
output_client_key.parameters.glwe_noise_distribution(),
),
EncryptionKeyChoice::Small => (
output_client_key.small_lwe_secret_key(),
output_client_key.parameters.lwe_noise_distribution(),
),
};
// Creation of the key switching key
allocate_and_generate_new_seeded_lwe_keyswitch_key(
&input_secret_key.lwe_secret_key,
&output_secret_key,
params.ks_base_log,
params.ks_level,
encryption_noise,
output_client_key.parameters.ciphertext_modulus(),
&mut self.seeder,
)
}
pub(crate) fn new_compressed_server_key(&mut self, cks: &ClientKey) -> CompressedServerKey {
// Plaintext Max Value
let max_value = cks.parameters.message_modulus().0 * cks.parameters.carry_modulus().0 - 1;

View File

@@ -2,7 +2,10 @@
//!
//! - [KeySwitchingKey] allows switching the keys of a ciphertext, from a cleitn key to another.
use crate::core_crypto::prelude::{keyswitch_lwe_ciphertext, LweKeyswitchKeyOwned};
use crate::core_crypto::prelude::{
decompress_seeded_lwe_keyswitch_key, keyswitch_lwe_ciphertext, ActivatedRandomGenerator,
LweKeyswitchKeyOwned, SeededLweKeyswitchKeyOwned,
};
use crate::shortint::ciphertext::Degree;
use crate::shortint::client_key::secret_encryption_key::SecretEncryptionKeyView;
use crate::shortint::engine::ShortintEngine;
@@ -10,62 +13,83 @@ use crate::shortint::parameters::{
EncryptionKeyChoice, NoiseLevel, PBSOrder, ShortintKeySwitchingParameters,
};
use crate::shortint::server_key::apply_programmable_bootstrap;
use crate::shortint::{Ciphertext, ClientKey, ServerKey};
use crate::shortint::{Ciphertext, ClientKey, CompressedServerKey, ServerKey};
use core::cmp::Ordering;
use serde::{Deserialize, Serialize};
#[cfg(test)]
mod test;
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct KeySwitchingKeyMaterial {
pub(crate) key_switching_key: LweKeyswitchKeyOwned<u64>,
pub(crate) cast_rshift: i8,
pub(crate) destination_key: EncryptionKeyChoice,
}
impl KeySwitchingKeyMaterial {
pub fn as_view(&self) -> KeySwitchingKeyMaterialView<'_> {
KeySwitchingKeyMaterialView {
key_switching_key: &self.key_switching_key,
cast_rshift: self.cast_rshift,
destination_key: self.destination_key,
}
}
}
// This is used to have the ability to build a keyswitching key without owning the ServerKey
// It is a bit of a hack, but at this point it seems ok
pub(crate) struct KeySwitchingKeyBuildHelper<'keys> {
pub(crate) key_switching_key_material: KeySwitchingKeyMaterial,
pub(crate) dest_server_key: &'keys ServerKey,
pub(crate) src_server_key: Option<&'keys ServerKey>,
}
/// A structure containing the casting public key.
///
/// The casting key is generated by the client and is meant to be published: the client
/// sends it to the server so it can cast from one set of parameters to another.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct KeySwitchingKey {
pub(crate) key_switching_key: LweKeyswitchKeyOwned<u64>,
pub(crate) key_switching_key_material: KeySwitchingKeyMaterial,
pub(crate) dest_server_key: ServerKey,
pub(crate) src_server_key: Option<ServerKey>,
pub cast_rshift: i8,
pub destination_key: EncryptionKeyChoice,
}
impl<'keys> From<KeySwitchingKeyBuildHelper<'keys>> for KeySwitchingKey {
fn from(value: KeySwitchingKeyBuildHelper) -> Self {
let KeySwitchingKeyBuildHelper {
key_switching_key_material,
dest_server_key,
src_server_key,
} = value;
Self {
key_switching_key_material,
dest_server_key: dest_server_key.to_owned(),
src_server_key: src_server_key.map(ToOwned::to_owned),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct KeySwitchingKeyMaterialView<'key> {
pub(crate) key_switching_key: &'key LweKeyswitchKeyOwned<u64>,
pub(crate) cast_rshift: i8,
pub(crate) destination_key: EncryptionKeyChoice,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct KeySwitchingKeyView<'keys> {
pub(crate) key_switching_key: &'keys LweKeyswitchKeyOwned<u64>,
pub(crate) key_switching_key_material: KeySwitchingKeyMaterialView<'keys>,
pub(crate) dest_server_key: &'keys ServerKey,
pub(crate) src_server_key: Option<&'keys ServerKey>,
pub cast_rshift: i8,
pub destination_key: EncryptionKeyChoice,
}
impl KeySwitchingKey {
/// Generate a casting key. This can cast to several kinds of keys (shortint, integer, hlapi),
/// depending on input.
///
/// # Example
///
/// ```rust
/// use tfhe::shortint::parameters::{
/// PARAM_MESSAGE_1_CARRY_1_KS_PBS, PARAM_MESSAGE_2_CARRY_2_KS_PBS,
/// };
/// use tfhe::shortint::prelude::*;
/// use tfhe::shortint::{gen_keys, KeySwitchingKey};
///
/// // Generate the client keys and server keys:
/// let (ck1, sk1) = gen_keys(PARAM_MESSAGE_1_CARRY_1_KS_PBS);
/// let (ck2, sk2) = gen_keys(PARAM_MESSAGE_2_CARRY_2_KS_PBS);
///
/// // Generate the server key:
/// let ksk = KeySwitchingKey::new(
/// (&ck1, Some(&sk1)),
/// (&ck2, &sk2),
/// PARAM_KEYSWITCH_1_1_KS_PBS_TO_2_2_KS_PBS,
/// );
/// ```
pub fn new<'input_key, InputEncryptionKey>(
input_key_pair: (InputEncryptionKey, Option<&ServerKey>),
output_key_pair: (&ClientKey, &ServerKey),
impl<'keys> KeySwitchingKeyBuildHelper<'keys> {
pub(crate) fn new<'input_key, InputEncryptionKey>(
input_key_pair: (InputEncryptionKey, Option<&'keys ServerKey>),
output_key_pair: (&'keys ClientKey, &'keys ServerKey),
params: ShortintKeySwitchingParameters,
) -> Self
where
@@ -102,57 +126,75 @@ impl KeySwitchingKey {
// Pack the keys in the casting key set:
Self {
key_switching_key,
dest_server_key: output_key_pair.1.clone(),
src_server_key: input_key_pair.1.cloned(),
cast_rshift: nb_bits_output - nb_bits_input,
destination_key: params.destination_key,
key_switching_key_material: KeySwitchingKeyMaterial {
key_switching_key,
cast_rshift: nb_bits_output - nb_bits_input,
destination_key: params.destination_key,
},
dest_server_key: output_key_pair.1,
src_server_key: input_key_pair.1,
}
}
}
impl KeySwitchingKey {
/// Generate a casting key. This can cast to several kinds of keys (shortint, integer, hlapi),
/// depending on input.
///
/// # Example
///
/// ```rust
/// use tfhe::shortint::parameters::{
/// PARAM_MESSAGE_1_CARRY_1_KS_PBS, PARAM_MESSAGE_2_CARRY_2_KS_PBS,
/// };
/// use tfhe::shortint::prelude::*;
/// use tfhe::shortint::{gen_keys, KeySwitchingKey};
///
/// // Generate the client keys and server keys:
/// let (ck1, sk1) = gen_keys(PARAM_MESSAGE_1_CARRY_1_KS_PBS);
/// let (ck2, sk2) = gen_keys(PARAM_MESSAGE_2_CARRY_2_KS_PBS);
///
/// // Generate the server key:
/// let ksk = KeySwitchingKey::new(
/// (&ck1, Some(&sk1)),
/// (&ck2, &sk2),
/// PARAM_KEYSWITCH_1_1_KS_PBS_TO_2_2_KS_PBS,
/// );
/// ```
pub fn new<'input_key, InputEncryptionKey>(
input_key_pair: (InputEncryptionKey, Option<&ServerKey>),
output_key_pair: (&ClientKey, &ServerKey),
params: ShortintKeySwitchingParameters,
) -> Self
where
InputEncryptionKey: Into<SecretEncryptionKeyView<'input_key>>,
{
KeySwitchingKeyBuildHelper::new(input_key_pair, output_key_pair, params).into()
}
pub fn as_view(&self) -> KeySwitchingKeyView<'_> {
let Self {
key_switching_key,
key_switching_key_material,
dest_server_key,
src_server_key,
cast_rshift,
destination_key,
} = self;
KeySwitchingKeyView {
key_switching_key,
key_switching_key_material: key_switching_key_material.as_view(),
dest_server_key,
src_server_key: src_server_key.as_ref(),
cast_rshift: *cast_rshift,
destination_key: *destination_key,
}
}
/// Deconstruct a [`KeySwitchingKey`] into its constituents.
pub fn into_raw_parts(
self,
) -> (
LweKeyswitchKeyOwned<u64>,
ServerKey,
Option<ServerKey>,
i8,
EncryptionKeyChoice,
) {
pub fn into_raw_parts(self) -> (KeySwitchingKeyMaterial, ServerKey, Option<ServerKey>) {
let Self {
key_switching_key,
key_switching_key_material,
dest_server_key,
src_server_key,
cast_rshift,
destination_key,
} = self;
(
key_switching_key,
dest_server_key,
src_server_key,
cast_rshift,
destination_key,
)
(key_switching_key_material, dest_server_key, src_server_key)
}
/// Construct a [`KeySwitchingKey`] from its constituents.
@@ -164,17 +206,15 @@ impl KeySwitchingKey {
/// if the provided source [`ServerKey`] ciphertext
/// [`LweDimension`](`crate::core_crypto::commons::parameters::LweDimension`) does not match the
/// input [`LweDimension`](`crate::core_crypto::commons::parameters::LweDimension`) of the
/// provided [`LweKeyswitchKeyOwned`] or if the provided destination [`ServerKey`]
/// ciphertext [`LweDimension`](`crate::core_crypto::commons::parameters::LweDimension`)
/// does not match the output
/// [`LweDimension`](`crate::core_crypto::commons::parameters::LweDimension`) of the
/// provided [`LweKeyswitchKeyOwned`].
/// [`LweKeyswitchKeyOwned`] in the provided [`KeySwitchingKeyMaterial`] or if the provided
/// destination [`ServerKey`] ciphertext
/// [`LweDimension`](`crate::core_crypto::commons::parameters::LweDimension`) does not match
/// the output [`LweDimension`](`crate::core_crypto::commons::parameters::LweDimension`) of
/// the [`LweKeyswitchKeyOwned`] in the provided [`KeySwitchingKeyMaterial`].
pub fn from_raw_parts(
key_switching_key: LweKeyswitchKeyOwned<u64>,
key_switching_key_material: KeySwitchingKeyMaterial,
dest_server_key: ServerKey,
src_server_key: Option<ServerKey>,
cast_rshift: i8,
destination_key: EncryptionKeyChoice,
) -> Self {
match src_server_key {
Some(ref src_server_key) => {
@@ -182,11 +222,15 @@ impl KeySwitchingKey {
assert_eq!(
src_lwe_dimension,
key_switching_key.input_key_lwe_dimension(),
key_switching_key_material
.key_switching_key
.input_key_lwe_dimension(),
"Mismatch between the source ServerKey ciphertext LweDimension ({:?}) \
and the LweKeyswitchKey input LweDimension ({:?})",
src_lwe_dimension,
key_switching_key.input_key_lwe_dimension(),
key_switching_key_material
.key_switching_key
.input_key_lwe_dimension(),
);
assert_eq!(
@@ -197,40 +241,46 @@ impl KeySwitchingKey {
);
}
None => assert!(
cast_rshift >= 0,
key_switching_key_material.cast_rshift >= 0,
"Trying to build a shortint::KeySwitchingKey with a negative cast_rshift \
without providing a source ServerKey, this is not supported"
),
}
let dst_lwe_dimension = match destination_key {
let dst_lwe_dimension = match key_switching_key_material.destination_key {
EncryptionKeyChoice::Big => dest_server_key.bootstrapping_key.output_lwe_dimension(),
EncryptionKeyChoice::Small => dest_server_key.bootstrapping_key.input_lwe_dimension(),
};
assert_eq!(
dst_lwe_dimension,
key_switching_key.output_key_lwe_dimension(),
key_switching_key_material
.key_switching_key
.output_key_lwe_dimension(),
"Mismatch between the destination ServerKey ciphertext LweDimension ({:?}) \
and the LweKeyswitchKey output LweDimension ({:?})",
dst_lwe_dimension,
key_switching_key.output_key_lwe_dimension(),
key_switching_key_material
.key_switching_key
.output_key_lwe_dimension(),
);
assert_eq!(
key_switching_key.ciphertext_modulus(),
key_switching_key_material
.key_switching_key
.ciphertext_modulus(),
dest_server_key.ciphertext_modulus,
"Mismatch between the LweKeyswitchKey CiphertextModulus ({:?}) \
and the destination ServerKey CiphertextModulus ({:?})",
key_switching_key.ciphertext_modulus(),
key_switching_key_material
.key_switching_key
.ciphertext_modulus(),
dest_server_key.ciphertext_modulus,
);
Self {
key_switching_key,
key_switching_key_material,
dest_server_key,
src_server_key,
cast_rshift,
destination_key,
}
}
@@ -270,6 +320,23 @@ impl KeySwitchingKey {
}
impl<'keys> KeySwitchingKeyView<'keys> {
/// Deconstruct a [`KeySwitchingKeyView`] into its constituents.
pub fn into_raw_parts(
self,
) -> (
KeySwitchingKeyMaterialView<'keys>,
&'keys ServerKey,
Option<&'keys ServerKey>,
) {
let Self {
key_switching_key_material,
dest_server_key,
src_server_key,
} = self;
(key_switching_key_material, dest_server_key, src_server_key)
}
/// Construct a [`KeySwitchingKeyView`] from its constituents.
///
/// # Panics
@@ -279,17 +346,15 @@ impl<'keys> KeySwitchingKeyView<'keys> {
/// if the provided source [`ServerKey`] ciphertext
/// [`LweDimension`](`crate::core_crypto::commons::parameters::LweDimension`) does not match the
/// input [`LweDimension`](`crate::core_crypto::commons::parameters::LweDimension`) of the
/// provided [`LweKeyswitchKeyOwned`] or if the provided destination [`ServerKey`]
/// ciphertext [`LweDimension`](`crate::core_crypto::commons::parameters::LweDimension`)
/// does not match the output
/// [`LweDimension`](`crate::core_crypto::commons::parameters::LweDimension`) of the
/// provided [`LweKeyswitchKeyOwned`].
pub fn try_new(
key_switching_key: &'keys LweKeyswitchKeyOwned<u64>,
/// [`LweKeyswitchKeyOwned`] in the provided [`KeySwitchingKeyMaterial`] or if the provided
/// destination [`ServerKey`] ciphertext
/// [`LweDimension`](`crate::core_crypto::commons::parameters::LweDimension`) does not match
/// the output [`LweDimension`](`crate::core_crypto::commons::parameters::LweDimension`) of
/// the [`LweKeyswitchKeyOwned`] in the provided [`KeySwitchingKeyMaterial`].
pub fn from_raw_parts(
key_switching_key_material: KeySwitchingKeyMaterialView<'keys>,
dest_server_key: &'keys ServerKey,
src_server_key: Option<&'keys ServerKey>,
cast_rshift: i8,
destination_key: EncryptionKeyChoice,
) -> Self {
match src_server_key {
Some(src_server_key) => {
@@ -297,11 +362,15 @@ impl<'keys> KeySwitchingKeyView<'keys> {
assert_eq!(
src_lwe_dimension,
key_switching_key.input_key_lwe_dimension(),
key_switching_key_material
.key_switching_key
.input_key_lwe_dimension(),
"Mismatch between the source ServerKey ciphertext LweDimension ({:?}) \
and the LweKeyswitchKey input LweDimension ({:?})",
src_lwe_dimension,
key_switching_key.input_key_lwe_dimension(),
key_switching_key_material
.key_switching_key
.input_key_lwe_dimension(),
);
assert_eq!(
@@ -312,40 +381,46 @@ impl<'keys> KeySwitchingKeyView<'keys> {
);
}
None => assert!(
cast_rshift >= 0,
key_switching_key_material.cast_rshift >= 0,
"Trying to build a shortint::KeySwitchingKey with a negative cast_rshift \
without providing a source ServerKey, this is not supported"
),
}
let dst_lwe_dimension = match destination_key {
let dst_lwe_dimension = match key_switching_key_material.destination_key {
EncryptionKeyChoice::Big => dest_server_key.bootstrapping_key.output_lwe_dimension(),
EncryptionKeyChoice::Small => dest_server_key.bootstrapping_key.input_lwe_dimension(),
};
assert_eq!(
dst_lwe_dimension,
key_switching_key.output_key_lwe_dimension(),
key_switching_key_material
.key_switching_key
.output_key_lwe_dimension(),
"Mismatch between the destination ServerKey ciphertext LweDimension ({:?}) \
and the LweKeyswitchKey output LweDimension ({:?})",
dst_lwe_dimension,
key_switching_key.output_key_lwe_dimension(),
key_switching_key_material
.key_switching_key
.output_key_lwe_dimension(),
);
assert_eq!(
key_switching_key.ciphertext_modulus(),
key_switching_key_material
.key_switching_key
.ciphertext_modulus(),
dest_server_key.ciphertext_modulus,
"Mismatch between the LweKeyswitchKey CiphertextModulus ({:?}) \
and the destination ServerKey CiphertextModulus ({:?})",
key_switching_key.ciphertext_modulus(),
key_switching_key_material
.key_switching_key
.ciphertext_modulus(),
dest_server_key.ciphertext_modulus,
);
Self {
key_switching_key,
key_switching_key_material,
dest_server_key,
src_server_key,
cast_rshift,
destination_key,
}
}
@@ -380,7 +455,7 @@ impl<'keys> KeySwitchingKeyView<'keys> {
/// assert_eq!(ck2.decrypt(&cipher_2), cleartext);
/// ```
pub fn cast(&self, input_ct: &Ciphertext) -> Ciphertext {
let output_lwe_size = match self.destination_key {
let output_lwe_size = match self.key_switching_key_material.destination_key {
EncryptionKeyChoice::Big => self
.dest_server_key
.bootstrapping_key
@@ -396,19 +471,27 @@ impl<'keys> KeySwitchingKeyView<'keys> {
.dest_server_key
.unchecked_create_trivial_with_lwe_size(0, output_lwe_size);
let cast_rshift = self.cast_rshift;
let cast_rshift = self.key_switching_key_material.cast_rshift;
match cast_rshift.cmp(&0) {
// Same bit size: only key switch
Ordering::Equal => {
keyswitch_lwe_ciphertext(self.key_switching_key, &input_ct.ct, &mut keyswitched.ct);
keyswitch_lwe_ciphertext(
self.key_switching_key_material.key_switching_key,
&input_ct.ct,
&mut keyswitched.ct,
);
keyswitched.degree = input_ct.degree;
// We don't really know where we stand in terms of noise here
keyswitched.set_noise_level(NoiseLevel::UNKNOWN);
}
// Cast to bigger bit length: keyswitch, then right shift
Ordering::Greater => {
keyswitch_lwe_ciphertext(self.key_switching_key, &input_ct.ct, &mut keyswitched.ct);
keyswitch_lwe_ciphertext(
self.key_switching_key_material.key_switching_key,
&input_ct.ct,
&mut keyswitched.ct,
);
let acc = self
.dest_server_key
@@ -432,7 +515,7 @@ impl<'keys> KeySwitchingKeyView<'keys> {
let shifted_cipher = src_server_key.apply_lookup_table(input_ct, &acc);
keyswitch_lwe_ciphertext(
self.key_switching_key,
self.key_switching_key_material.key_switching_key,
&shifted_cipher.ct,
&mut keyswitched.ct,
);
@@ -452,7 +535,8 @@ impl<'keys> KeySwitchingKeyView<'keys> {
}
let ret = {
let destination_pbs_order: PBSOrder = self.destination_key.into();
let destination_pbs_order: PBSOrder =
self.key_switching_key_material.destination_key.into();
if destination_pbs_order == self.dest_server_key.pbs_order {
keyswitched
} else {
@@ -462,7 +546,7 @@ impl<'keys> KeySwitchingKeyView<'keys> {
output.set_noise_level(wrong_key_ct.noise_level());
// We are arriving under the wrong key for the dest_server_key
match self.destination_key {
match self.key_switching_key_material.destination_key {
// Big to Small == keyswitch
EncryptionKeyChoice::Big => {
keyswitch_lwe_ciphertext(
@@ -496,3 +580,147 @@ impl<'keys> KeySwitchingKeyView<'keys> {
ret
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CompressedKeySwitchingKeyMaterial {
pub(crate) key_switching_key: SeededLweKeyswitchKeyOwned<u64>,
pub(crate) cast_rshift: i8,
pub(crate) destination_key: EncryptionKeyChoice,
}
impl CompressedKeySwitchingKeyMaterial {
pub fn decompress(&self) -> KeySwitchingKeyMaterial {
let key_switching_key = {
let cksk = &self.key_switching_key;
let mut decompressed_ksk = LweKeyswitchKeyOwned::new(
0u64,
cksk.decomposition_base_log(),
cksk.decomposition_level_count(),
cksk.input_key_lwe_dimension(),
cksk.output_key_lwe_dimension(),
cksk.ciphertext_modulus(),
);
decompress_seeded_lwe_keyswitch_key::<_, _, _, ActivatedRandomGenerator>(
&mut decompressed_ksk,
cksk,
);
decompressed_ksk
};
KeySwitchingKeyMaterial {
key_switching_key,
cast_rshift: self.cast_rshift,
destination_key: self.destination_key,
}
}
}
// This is used to have the ability to build a keyswitching key without owning the ServerKey
// It is a bit of a hack, but at this point it seems ok
pub(crate) struct CompressedKeySwitchingKeyBuildHelper<'keys> {
pub(crate) key_switching_key_material: CompressedKeySwitchingKeyMaterial,
pub(crate) dest_server_key: &'keys CompressedServerKey,
pub(crate) src_server_key: Option<&'keys CompressedServerKey>,
}
/// A structure containing the casting public key.
///
/// The casting key is generated by the client and is meant to be published: the client
/// sends it to the server so it can cast from one set of parameters to another.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CompressedKeySwitchingKey {
pub(crate) key_switching_key_material: CompressedKeySwitchingKeyMaterial,
pub(crate) dest_server_key: CompressedServerKey,
pub(crate) src_server_key: Option<CompressedServerKey>,
}
impl<'keys> From<CompressedKeySwitchingKeyBuildHelper<'keys>> for CompressedKeySwitchingKey {
fn from(value: CompressedKeySwitchingKeyBuildHelper) -> Self {
let CompressedKeySwitchingKeyBuildHelper {
key_switching_key_material,
dest_server_key,
src_server_key,
} = value;
Self {
key_switching_key_material,
dest_server_key: dest_server_key.to_owned(),
src_server_key: src_server_key.map(ToOwned::to_owned),
}
}
}
impl<'keys> CompressedKeySwitchingKeyBuildHelper<'keys> {
pub(crate) fn new<'input_key, InputEncryptionKey>(
input_key_pair: (InputEncryptionKey, Option<&'keys CompressedServerKey>),
output_key_pair: (&'keys ClientKey, &'keys CompressedServerKey),
params: ShortintKeySwitchingParameters,
) -> Self
where
InputEncryptionKey: Into<SecretEncryptionKeyView<'input_key>>,
{
let input_secret_key: SecretEncryptionKeyView<'_> = input_key_pair.0.into();
// Creation of the key switching key
let key_switching_key = ShortintEngine::with_thread_local_mut(|engine| {
engine.new_seeded_key_switching_key(&input_secret_key, output_key_pair.0, params)
});
let full_message_modulus_input =
input_secret_key.carry_modulus.0 * input_secret_key.message_modulus.0;
let full_message_modulus_output = output_key_pair.0.parameters.carry_modulus().0
* output_key_pair.0.parameters.message_modulus().0;
assert!(
full_message_modulus_input.is_power_of_two()
&& full_message_modulus_output.is_power_of_two(),
"Cannot create casting key if the full messages moduli are not a power of 2"
);
if full_message_modulus_input > full_message_modulus_output {
assert!(
input_key_pair.1.is_some(),
"Trying to build a shortint::KeySwitchingKey \
going from a large modulus {full_message_modulus_input} \
to a smaller modulus {full_message_modulus_output} \
without providing a source ServerKey, this is not supported"
);
}
let nb_bits_input: i8 = full_message_modulus_input.ilog2().try_into().unwrap();
let nb_bits_output: i8 = full_message_modulus_output.ilog2().try_into().unwrap();
// Pack the keys in the casting key set:
Self {
key_switching_key_material: CompressedKeySwitchingKeyMaterial {
key_switching_key,
cast_rshift: nb_bits_output - nb_bits_input,
destination_key: params.destination_key,
},
dest_server_key: output_key_pair.1,
src_server_key: input_key_pair.1,
}
}
}
impl CompressedKeySwitchingKey {
pub fn new<'input_key, InputEncryptionKey>(
input_key_pair: (InputEncryptionKey, Option<&CompressedServerKey>),
output_key_pair: (&ClientKey, &CompressedServerKey),
params: ShortintKeySwitchingParameters,
) -> Self
where
InputEncryptionKey: Into<SecretEncryptionKeyView<'input_key>>,
{
CompressedKeySwitchingKeyBuildHelper::new(input_key_pair, output_key_pair, params).into()
}
pub fn decompress(&self) -> KeySwitchingKey {
KeySwitchingKey {
key_switching_key_material: self.key_switching_key_material.decompress(),
dest_server_key: self.dest_server_key.decompress(),
src_server_key: self
.src_server_key
.as_ref()
.map(CompressedServerKey::decompress),
}
}
}

View File

@@ -13,7 +13,7 @@ fn gen_multi_keys_test_fresh_ci_run_filter() {
let (ck2, sk2) = (keys.client_key_2(), keys.server_key_2());
let ksk = keys.key_switching_key();
assert_eq!(ksk.cast_rshift, 2);
assert_eq!(ksk.key_switching_key_material.cast_rshift, 2);
// Message 0 Carry 0
let cipher = ck1.encrypt(0);
@@ -71,7 +71,7 @@ fn gen_multi_keys_test_fresh_2_ci_run_filter() {
let ck1 = keys.client_key_1();
let ksk = keys.key_switching_key();
assert_eq!(ksk.cast_rshift, 4);
assert_eq!(ksk.key_switching_key_material.cast_rshift, 4);
// Message 0 Carry 0
let cipher = ck1.encrypt(0);
@@ -154,7 +154,7 @@ fn gen_multi_keys_test_no_shift_ci_run_filter() {
));
let ksk = keys.key_switching_key();
assert_eq!(ksk.cast_rshift, 0);
assert_eq!(ksk.key_switching_key_material.cast_rshift, 0);
}
#[test]
@@ -176,7 +176,7 @@ fn gen_multi_keys_test_truncate_ci_run_filter() {
let (ck1, sk1) = (keys.client_key_1(), keys.server_key_1());
let ksk = keys.key_switching_key();
assert_eq!(ksk.cast_rshift, -2);
assert_eq!(ksk.key_switching_key_material.cast_rshift, -2);
// Message 0 Carry 0
let cipher = ck1.unchecked_encrypt(0);

View File

@@ -1,6 +1,7 @@
use crate::keycache::utils::named_params_impl;
use crate::keycache::*;
use crate::shortint::parameters::classic::compact_pk::*;
use crate::shortint::parameters::classic::tuniform::p_fail_2_minus_64::ks_pbs::PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
#[cfg(tarpaulin)]
use crate::shortint::parameters::coverage_parameters::*;
use crate::shortint::parameters::key_switching::*;

View File

@@ -62,7 +62,7 @@ pub mod wopbs;
pub use ciphertext::{Ciphertext, CompressedCiphertext, PBSOrder};
pub use client_key::ClientKey;
pub use key_switching_key::{KeySwitchingKey, KeySwitchingKeyView};
pub use key_switching_key::{CompressedKeySwitchingKey, KeySwitchingKey, KeySwitchingKeyView};
pub use parameters::{
CarryModulus, CiphertextModulus, ClassicPBSParameters, EncryptionKeyChoice, MaxNoiseLevel,
MessageModulus, MultiBitPBSParameters, PBSParameters, ShortintParameterSet, WopbsParameters,

View File

@@ -1,6 +1,9 @@
use super::{CiphertextModulus, PBSOrder};
use crate::core_crypto::commons::parameters::{DynamicDistribution, LweDimension};
use crate::shortint::parameters::{CarryModulus, MessageModulus, ShortintParameterSet};
use crate::shortint::parameters::{
CarryModulus, ClassicPBSParameters, MessageModulus, MultiBitPBSParameters, PBSParameters,
ShortintParameterSet,
};
use crate::shortint::KeySwitchingKeyView;
use crate::Error;
use serde::{Deserialize, Serialize};
@@ -113,3 +116,41 @@ impl TryFrom<ShortintParameterSet> for CompactPublicKeyEncryptionParameters {
)
}
}
impl TryFrom<ClassicPBSParameters> for CompactPublicKeyEncryptionParameters {
type Error = Error;
fn try_from(value: ClassicPBSParameters) -> Result<Self, Self::Error> {
let params: PBSParameters = value.into();
params.try_into()
}
}
impl TryFrom<MultiBitPBSParameters> for CompactPublicKeyEncryptionParameters {
type Error = Error;
fn try_from(value: MultiBitPBSParameters) -> Result<Self, Self::Error> {
let params: PBSParameters = value.into();
params.try_into()
}
}
impl TryFrom<PBSParameters> for CompactPublicKeyEncryptionParameters {
type Error = Error;
fn try_from(value: PBSParameters) -> Result<Self, Self::Error> {
let params: ShortintParameterSet = value.into();
params.try_into()
}
}
pub const PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64: CompactPublicKeyEncryptionParameters =
CompactPublicKeyEncryptionParameters {
encryption_lwe_dimension: LweDimension(1024),
encryption_noise_distribution: DynamicDistribution::new_t_uniform(42),
message_modulus: MessageModulus(4),
carry_modulus: CarryModulus(4),
ciphertext_modulus: CiphertextModulus::new_native(),
expansion_kind: CompactCiphertextListExpansionKind::RequiresCasting,
}
.validate();

View File

@@ -39,3 +39,10 @@ pub const PARAM_KEYSWITCH_1_1_KS_PBS_TO_2_2_KS_PBS: ShortintKeySwitchingParamete
ks_base_log: DecompositionBaseLog(1),
destination_key: EncryptionKeyChoice::Big,
};
pub const PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64: ShortintKeySwitchingParameters =
ShortintKeySwitchingParameters {
ks_level: DecompositionLevelCount(5),
ks_base_log: DecompositionBaseLog(3),
destination_key: EncryptionKeyChoice::Small,
};

View File

@@ -25,8 +25,6 @@ pub mod multi_bit;
pub mod parameters_wopbs;
pub mod parameters_wopbs_message_carry;
pub mod parameters_wopbs_only;
#[cfg(test)]
pub mod test_parameters;
pub use super::ciphertext::{Degree, MaxNoiseLevel, NoiseLevel};
pub use super::PBSOrder;
@@ -38,7 +36,8 @@ pub use crate::shortint::parameters::classic::gaussian::p_fail_2_minus_64::pbs_k
pub use crate::shortint::parameters::classic::tuniform::p_fail_2_minus_64::ks_pbs::*;
pub use crate::shortint::parameters::classic::tuniform::p_fail_2_minus_64::pbs_ks::*;
pub use compact_public_key_only::{
CompactCiphertextListExpansionKind, ShortintCompactCiphertextListCastingMode,
CompactCiphertextListExpansionKind, CompactPublicKeyEncryptionParameters,
ShortintCompactCiphertextListCastingMode,
};
#[cfg(tarpaulin)]
pub use coverage_parameters::*;

View File

@@ -1,28 +0,0 @@
//! #Warning test-only
//!
//! This module provides the structure containing the cryptographic parameters only intended to be
//! used to test some operations.
//! These parameters are *NOT guaranteed to be safe*.
use crate::core_crypto::prelude::*;
use crate::shortint::ciphertext::MaxNoiseLevel;
use crate::shortint::parameters::{CarryModulus, ClassicPBSParameters, MessageModulus};
// TODO To remove once casting is available
pub const PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS_TUNIFORM_2M64: ClassicPBSParameters =
ClassicPBSParameters {
lwe_dimension: LweDimension(1024),
glwe_dimension: GlweDimension(1),
polynomial_size: PolynomialSize(2048),
lwe_noise_distribution: DynamicDistribution::new_t_uniform(41),
glwe_noise_distribution: DynamicDistribution::new_t_uniform(14),
pbs_base_log: DecompositionBaseLog(23),
pbs_level: DecompositionLevelCount(1),
ks_base_log: DecompositionBaseLog(5),
ks_level: DecompositionLevelCount(4),
message_modulus: MessageModulus(4),
carry_modulus: CarryModulus(4),
max_noise_level: MaxNoiseLevel::new(5),
log2_p_fail: -66.873,
ciphertext_modulus: CiphertextModulus::new_native(),
encryption_key_choice: EncryptionKeyChoice::Small,
};

View File

@@ -81,14 +81,12 @@ impl CompactPrivateKey<Vec<u64>> {
}
}
impl<'key, C: Container<Element = u64>> TryFrom<&'key CompactPrivateKey<C>>
impl<'key, C: Container<Element = u64>> From<&'key CompactPrivateKey<C>>
for CompactPrivateKey<&'key [u64]>
{
type Error = crate::Error;
#[inline(always)]
fn try_from(value: &'key CompactPrivateKey<C>) -> Result<Self, Self::Error> {
Ok(value.as_view())
fn from(value: &'key CompactPrivateKey<C>) -> Self {
value.as_view()
}
}
@@ -155,18 +153,20 @@ fn to_plaintext_iterator(
}
impl CompactPublicKey {
pub fn new<'data, C>(compact_private_key: C) -> Self
pub fn new<'data, C, E>(compact_private_key: C) -> Self
where
C: TryInto<CompactPrivateKey<&'data [u64]>, Error = Error>,
C: TryInto<CompactPrivateKey<&'data [u64]>, Error = E>,
Error: From<E>,
{
Self::try_new(compact_private_key).expect(
"Incompatible parameters, the lwe_dimension of the secret key must be a power of two",
)
}
pub fn try_new<'data, C>(input_key: C) -> Result<Self, Error>
pub fn try_new<'data, C, E>(input_key: C) -> Result<Self, Error>
where
C: TryInto<CompactPrivateKey<&'data [u64]>, Error = Error>,
C: TryInto<CompactPrivateKey<&'data [u64]>, Error = E>,
Error: From<E>,
{
let compact_private_key: CompactPrivateKey<&[u64]> = input_key.try_into()?;

View File

@@ -76,7 +76,18 @@
value="Compact Public Key Bench 256 Bits Big"
disabled
/>
<input
type="button"
id="compactPublicKeyWithCastingTest256Bit"
value="Compact Public Key With Casting Test 256 Bit"
disabled
/>
<input
type="button"
id="compressedCompactPublicKeyWithCastingTest256Bit"
value="Compressed Compact Public Key With Casting Test 256 Bit"
disabled
/>
<input
type="button"
id="compressedServerKeyBenchMessage1Carry1"

View File

@@ -32,6 +32,8 @@ async function setup() {
"compactPublicKeyBench32BitSmall",
"compactPublicKeyBench256BitBig",
"compactPublicKeyBench256BitSmall",
"compactPublicKeyWithCastingTest256Bit",
"compressedCompactPublicKeyWithCastingTest256Bit",
"compressedServerKeyBenchMessage1Carry1",
"compressedServerKeyBenchMessage2Carry2",
"compactPublicKeyZeroKnowledgeBench",

View File

@@ -2891,9 +2891,9 @@
"dev": true
},
"node_modules/@zeit/schemas": {
"version": "2.29.0",
"resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.29.0.tgz",
"integrity": "sha512-g5QiLIfbg3pLuYUJPlisNKY+epQJTcMDsOnVNkscrDP1oi7vmJnzOANYJI/1pZcVJ6umUkBv3aFtlg1UvUHGzA==",
"version": "2.36.0",
"resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.36.0.tgz",
"integrity": "sha512-7kjMwcChYEzMKjeex9ZFXkt1AyNov9R5HZtjBKVsmVpw7pa7ZtlCGvCBC2vnnXctaYN+aRI61HjIqeetZW5ROg==",
"dev": true
},
"node_modules/accepts": {
@@ -2943,9 +2943,9 @@
}
},
"node_modules/ajv": {
"version": "8.11.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
"integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
"version": "8.12.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
"dev": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
@@ -7801,13 +7801,13 @@
}
},
"node_modules/serve": {
"version": "14.2.1",
"resolved": "https://registry.npmjs.org/serve/-/serve-14.2.1.tgz",
"integrity": "sha512-48er5fzHh7GCShLnNyPBRPEjs2I6QBozeGr02gaacROiyS/8ARADlj595j39iZXAqBbJHH/ivJJyPRWY9sQWZA==",
"version": "14.2.3",
"resolved": "https://registry.npmjs.org/serve/-/serve-14.2.3.tgz",
"integrity": "sha512-VqUFMC7K3LDGeGnJM9h56D3XGKb6KGgOw0cVNtA26yYXHCcpxf3xwCTUaQoWlVS7i8Jdh3GjQkOB23qsXyjoyQ==",
"dev": true,
"dependencies": {
"@zeit/schemas": "2.29.0",
"ajv": "8.11.0",
"@zeit/schemas": "2.36.0",
"ajv": "8.12.0",
"arg": "5.0.2",
"boxen": "7.0.0",
"chalk": "5.0.1",

View File

@@ -8,6 +8,16 @@ it("Compressed Compact Public Key Test Big 256 Bit", async () => {
await runTestAttachedToButton("compressedCompactPublicKeyTest256BitBig");
});
it("Compact Public Key With Casting Test 256 Bit", async () => {
await runTestAttachedToButton("compactPublicKeyWithCastingTest256Bit");
});
it("Compressed Compact Public Key With Casting Test 256 Bit", async () => {
await runTestAttachedToButton(
"compressedCompactPublicKeyWithCastingTest256Bit",
);
});
it(
"Compact Public Key Test Big 64 Bit With Zero Knowledge",
async () => {

View File

@@ -16,8 +16,8 @@ import init, {
CompactPkeCrs,
CompactCiphertextList,
ProvenCompactCiphertextList,
Shortint,
ShortintEncryptionKeyChoice,
ShortintCompactPublicKeyEncryptionParameters,
ShortintCompactPublicKeyEncryptionParametersName,
} from "./pkg/tfhe.js";
const U32_MAX = 4294967295;
@@ -240,27 +240,106 @@ async function compressedCompactPublicKeyTest256BitOnConfig(config) {
assert_eq(expander.get_uint256(3).decrypt(clientKey), clear_u256);
}
async function compactPublicKeyZeroKnowledge() {
let block_params = Shortint.new_parameters(
1024,
1,
2048,
Shortint.try_new_t_uniform(41),
Shortint.try_new_t_uniform(14),
23,
1,
5,
4,
4,
4,
5,
-66.873,
64,
ShortintEncryptionKeyChoice.Small,
async function compactPublicKeyWithCastingTest256Bit() {
let block_params = new ShortintParameters(
ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64,
);
let casting_params = new ShortintCompactPublicKeyEncryptionParameters(
ShortintCompactPublicKeyEncryptionParametersName.SHORTINT_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64,
);
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.use_dedicated_compact_public_key_parameters(casting_params)
.build();
let clientKey = TfheClientKey.generate(config);
let publicKey = TfheCompactPublicKey.new(clientKey);
let clear_u2 = 3;
let clear_i32 = -3284;
let clear_bool = true;
let clear_u256 = generateRandomBigInt(256);
let builder = CompactCiphertextList.builder(publicKey);
builder.push_u2(clear_u2);
builder.push_i32(clear_i32);
builder.push_boolean(clear_bool);
builder.push_u256(clear_u256);
let num_bits_encrypted = 2 + 4 + 1 + 256;
console.log("Numb bits in compact list: ", num_bits_encrypted);
console.time("CompactCiphertextList Encrypt");
let list = builder.build();
console.timeEnd("CompactCiphertextList Encrypt");
let serialized = list.safe_serialize(BigInt(10000000));
console.log("Serialized CompactCiphertextList size: ", serialized.length);
let deserialized = CompactCiphertextList.safe_deserialize(
serialized,
BigInt(10000000),
);
// Cannot expand
}
async function compressedCompactPublicKeyWithCastingTest256Bit() {
let block_params = new ShortintParameters(
ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64,
);
let casting_params = new ShortintCompactPublicKeyEncryptionParameters(
ShortintCompactPublicKeyEncryptionParametersName.SHORTINT_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64,
);
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.use_dedicated_compact_public_key_parameters(casting_params)
.build();
let clientKey = TfheClientKey.generate(config);
let compressedPublicKey = TfheCompressedCompactPublicKey.new(clientKey);
let publicKey = compressedPublicKey.decompress();
let clear_u2 = 3;
let clear_i32 = -3284;
let clear_bool = true;
let clear_u256 = generateRandomBigInt(256);
let builder = CompactCiphertextList.builder(publicKey);
builder.push_u2(clear_u2);
builder.push_i32(clear_i32);
builder.push_boolean(clear_bool);
builder.push_u256(clear_u256);
let num_bits_encrypted = 2 + 4 + 1 + 256;
console.log("Numb bits in compact list: ", num_bits_encrypted);
console.time("CompactCiphertextList Encrypt");
let list = builder.build();
console.timeEnd("CompactCiphertextList Encrypt");
let serialized = list.safe_serialize(BigInt(10000000));
console.log("Serialized CompactCiphertextList size: ", serialized.length);
let deserialized = CompactCiphertextList.safe_deserialize(
serialized,
BigInt(10000000),
);
// Cannot expand
}
async function compactPublicKeyZeroKnowledge() {
let block_params = new ShortintParameters(
ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64,
);
let casting_params = new ShortintCompactPublicKeyEncryptionParameters(
ShortintCompactPublicKeyEncryptionParametersName.SHORTINT_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64,
);
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.use_dedicated_compact_public_key_parameters(casting_params)
.build();
let clientKey = TfheClientKey.generate(config);
@@ -490,26 +569,14 @@ async function compressedServerKeyBenchMessage2Carry2() {
}
async function compactPublicKeyZeroKnowledgeBench() {
// TODO replace by params with casting
let params_to_bench = [
{
name: "PARAM_MESSAGE_2_CARRY_2_PBS_KS_TUNIFORM_2M64",
params: Shortint.new_parameters(
1024,
1,
2048,
Shortint.try_new_t_uniform(41),
Shortint.try_new_t_uniform(14),
23,
1,
5,
4,
4,
4,
5,
-66.873,
64,
ShortintEncryptionKeyChoice.Small,
name: "PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64",
block_params: new ShortintParameters(
ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64,
),
casting_params: new ShortintCompactPublicKeyEncryptionParameters(
ShortintCompactPublicKeyEncryptionParametersName.SHORTINT_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64,
),
},
];
@@ -518,10 +585,12 @@ async function compactPublicKeyZeroKnowledgeBench() {
for (const params of params_to_bench) {
let block_params_name = params.name;
let block_params = params.params;
let block_params = params.block_params;
let casting_params = params.casting_params;
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.use_dedicated_compact_public_key_parameters(casting_params)
.build();
let clientKey = TfheClientKey.generate(config);
@@ -571,13 +640,11 @@ async function compactPublicKeyZeroKnowledgeBench() {
"compact_fhe_uint_proven_encryption_" +
encrypt_count * 64 +
"_bits_packed_" +
load_to_str[loadChoice] +
"_mean_" +
block_params_name;
load_to_str[loadChoice];
const bench_str_1 = common_bench_str + "_mean_" + block_params_name;
console.log(bench_str_1, ": ", mean, " ms");
const bench_str_2 =
common_bench_str + "_serialized_size_" + block_params_name;
common_bench_str + "_serialized_size_mean_" + block_params_name;
console.log(bench_str_2, ": ", serialized_size, " bytes");
bench_results[bench_str_1] = mean;
@@ -604,6 +671,8 @@ async function main() {
compactPublicKeyBench32BitSmall,
compactPublicKeyBench256BitBig,
compactPublicKeyBench256BitSmall,
compactPublicKeyWithCastingTest256Bit,
compressedCompactPublicKeyWithCastingTest256Bit,
compressedServerKeyBenchMessage1Carry1,
compressedServerKeyBenchMessage2Carry2,
compactPublicKeyZeroKnowledgeBench,