mirror of
https://github.com/zama-ai/tfhe-rs.git
synced 2026-01-08 22:28:01 -05:00
feat(high_level_api): add casting primitives for compact public key
This commit is contained in:
2
Makefile
2
Makefile
@@ -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.
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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}'"),
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
(
|
||||
$(
|
||||
|
||||
@@ -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!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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(¶ms));
|
||||
}
|
||||
|
||||
#[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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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 {
|
||||
(
|
||||
$(
|
||||
|
||||
@@ -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| {
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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()?;
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -32,6 +32,8 @@ async function setup() {
|
||||
"compactPublicKeyBench32BitSmall",
|
||||
"compactPublicKeyBench256BitBig",
|
||||
"compactPublicKeyBench256BitSmall",
|
||||
"compactPublicKeyWithCastingTest256Bit",
|
||||
"compressedCompactPublicKeyWithCastingTest256Bit",
|
||||
"compressedServerKeyBenchMessage1Carry1",
|
||||
"compressedServerKeyBenchMessage2Carry2",
|
||||
"compactPublicKeyZeroKnowledgeBench",
|
||||
|
||||
22
tfhe/web_wasm_parallel_tests/package-lock.json
generated
22
tfhe/web_wasm_parallel_tests/package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user