Compare commits

...

18 Commits

Author SHA1 Message Date
Charlotte.Bonte
9575943ce1 change parameters test pfks 2023-06-01 16:34:19 +02:00
Charlotte.Bonte
3c65c9c38a change parameters test pfks 2023-06-01 16:25:18 +02:00
Charlotte.Bonte
0f4a5c6162 change parameters test pfks 2023-06-01 16:24:32 +02:00
Charlotte.Bonte
dba6535b17 change parameters test pfks 2023-05-31 21:57:54 +02:00
Charlotte.Bonte
ceeb968af1 change parameters test pfks 2023-05-31 21:45:51 +02:00
Charlotte.Bonte
bf1bd2226e test pfks 2023-05-31 18:29:17 +02:00
Charlotte.Bonte
34f2d81c9d split up tests 2023-05-31 18:15:15 +02:00
Charlotte.Bonte
5a59b1d32e split up tests 2023-05-31 17:42:03 +02:00
Charlotte.Bonte
1cb209cd93 add more parameter sets 2023-05-31 15:27:38 +02:00
Charlotte.Bonte
7eb2f47a58 switch parameters to make a comparison 2023-05-31 15:04:45 +02:00
Charlotte.Bonte
1082746e91 fix test for public functional key switching 2023-05-31 11:53:03 +02:00
Charlotte.Bonte
74fef3dad8 tix test public functional packing key switching 2023-05-31 10:53:19 +02:00
Charlotte.Bonte
d3dca9919b write test for public functional key switching 2023-05-31 10:42:18 +02:00
Charlotte.Bonte
54f4547340 write test for public functional key switching 2023-05-31 10:31:56 +02:00
Charlotte.Bonte
dd5407f9ce reduce the sample size 2023-05-30 13:40:34 +02:00
Charlotte.Bonte
f9c997adca activate the benches for the three functions, fix parameters 2023-05-30 13:09:17 +02:00
Charlotte.Bonte
cf854ee2fd add benchmarks for tensor product 2023-05-30 12:13:35 +02:00
Carl-Zama
7d07cef91b feat(tfhe): add glwe keyswitch, glwe tensor product, trace packing keyswitch and public functional packing keyswitch 2023-05-16 17:15:48 +01:00
25 changed files with 6076 additions and 46 deletions

View File

@@ -61,44 +61,44 @@ jobs:
run: |
make AVX512_SUPPORT=ON bench_pbs
- name: Parse results
run: |
COMMIT_DATE="$(git --no-pager show -s --format=%cd --date=iso8601-strict ${{ github.sha }})"
COMMIT_HASH="$(git describe --tags --dirty)"
python3 ./ci/benchmark_parser.py target/criterion ${{ env.RESULTS_FILENAME }} \
--database tfhe_rs \
--hardware ${{ inputs.instance_type }} \
--project-version "${COMMIT_HASH}" \
--branch ${{ github.ref_name }} \
--commit-date "${COMMIT_DATE}" \
--bench-date "${{ env.BENCH_DATE }}" \
--name-suffix avx512 \
--walk-subdirs \
--throughput
# - name: Parse results
# run: |
# COMMIT_DATE="$(git --no-pager show -s --format=%cd --date=iso8601-strict ${{ github.sha }})"
# COMMIT_HASH="$(git describe --tags --dirty)"
# python3 ./ci/benchmark_parser.py target/criterion ${{ env.RESULTS_FILENAME }} \
# --database tfhe_rs \
# --hardware ${{ inputs.instance_type }} \
# --project-version "${COMMIT_HASH}" \
# --branch ${{ github.ref_name }} \
# --commit-date "${COMMIT_DATE}" \
# --bench-date "${{ env.BENCH_DATE }}" \
# --name-suffix avx512 \
# --walk-subdirs \
# --throughput
- name: Upload parsed results artifact
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce
with:
name: ${{ github.sha }}_pbs
path: ${{ env.RESULTS_FILENAME }}
# - name: Upload parsed results artifact
# uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce
# with:
# name: ${{ github.sha }}_pbs
# path: ${{ env.RESULTS_FILENAME }}
#
# - name: Checkout Slab repo
# uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
# with:
# repository: zama-ai/slab
# path: slab
# token: ${{ secrets.CONCRETE_ACTIONS_TOKEN }}
- name: Checkout Slab repo
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
with:
repository: zama-ai/slab
path: slab
token: ${{ secrets.CONCRETE_ACTIONS_TOKEN }}
- name: Send data to Slab
shell: bash
run: |
echo "Computing HMac on downloaded artifact"
SIGNATURE="$(slab/scripts/hmac_calculator.sh ${{ env.RESULTS_FILENAME }} '${{ secrets.JOB_SECRET }}')"
echo "Sending results to Slab..."
curl -v -k \
-H "Content-Type: application/json" \
-H "X-Slab-Repository: ${{ github.repository }}" \
-H "X-Slab-Command: store_data_v2" \
-H "X-Hub-Signature-256: sha256=${SIGNATURE}" \
-d @${{ env.RESULTS_FILENAME }} \
${{ secrets.SLAB_URL }}
# - name: Send data to Slab
# shell: bash
# run: |
# echo "Computing HMac on downloaded artifact"
# SIGNATURE="$(slab/scripts/hmac_calculator.sh ${{ env.RESULTS_FILENAME }} '${{ secrets.JOB_SECRET }}')"
# echo "Sending results to Slab..."
# curl -v -k \
# -H "Content-Type: application/json" \
# -H "X-Slab-Repository: ${{ github.repository }}" \
# -H "X-Slab-Command: store_data_v2" \
# -H "X-Hub-Signature-256: sha256=${SIGNATURE}" \
# -d @${{ env.RESULTS_FILENAME }} \
# ${{ secrets.SLAB_URL }}

View File

@@ -12,7 +12,7 @@ jobs:
if: ${{ (github.event_name == 'push' && github.repository == 'zama-ai/tfhe-rs') || github.event_name == 'workflow_dispatch' }}
strategy:
matrix:
command: [boolean_bench, shortint_bench, integer_bench, pbs_bench]
command: [pbs_bench]
runs-on: ubuntu-latest
steps:
- name: Checkout Slab repo

View File

@@ -34,7 +34,7 @@ const BOOLEAN_BENCH_PARAMS: [(&str, BooleanParameters); 2] = [
criterion_group!(
name = pbs_group;
config = Criterion::default().sample_size(2000);
config = Criterion::default().sample_size(100);
targets = mem_optimized_pbs::<u64>, mem_optimized_pbs::<u32>
);
@@ -44,7 +44,31 @@ criterion_group!(
targets = multi_bit_pbs::<u64>, multi_bit_pbs::<u32>
);
criterion_main!(pbs_group, multi_bit_pbs_group);
criterion_group!(
name = tensor_prod_with_relin_group;
config = Criterion::default().sample_size(100);
targets = tensor_product_with_relin::<u64>
);
criterion_group!(
name = public_funct_ks_group;
config = Criterion::default().sample_size(100);
targets = public_funct_ks::<u64>
);
criterion_group!(
name = packed_mult_group;
config = Criterion::default().sample_size(100);
targets = packed_mul::<u64>
);
criterion_group!(
name = sum_of_products_group;
config = Criterion::default().sample_size(100);
targets = packed_sum_prod::<u64>
);
criterion_main!(pbs_group, tensor_prod_with_relin_group,public_funct_ks_group);
fn benchmark_parameters<Scalar: Numeric>() -> Vec<(String, CryptoParametersRecord)> {
if Scalar::BITS == 64 {
@@ -237,7 +261,7 @@ fn mem_optimized_pbs<Scalar: UnsignedTorus + CastInto<usize>>(c: &mut Criterion)
let mut secret_generator =
SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
for (name, params) in benchmark_parameters::<Scalar>().iter() {
for (name, params) in packed_CJP_optimized::<Scalar>().iter() {
// Create the LweSecretKey
let input_lwe_secret_key = allocate_and_generate_new_binary_lwe_secret_key(
params.lwe_dimension.unwrap(),
@@ -418,3 +442,748 @@ fn multi_bit_pbs<Scalar: UnsignedTorus + CastInto<usize> + CastFrom<usize> + Syn
);
}
}
//TODO check parameters
fn tensor_prod_with_relin_benchmark_parameters<Scalar: Numeric>(
) -> Vec<(String, CryptoParametersRecord)> {
if Scalar::BITS == 64 {
vec![
(
"1_bits_prec".to_string(),
(
CryptoParametersRecord {
lwe_dimension: Some(LweDimension(550)),
ks_base_log: Some(DecompositionBaseLog(8)),
ks_level: Some(DecompositionLevelCount(8)),
glwe_dimension: Some(GlweDimension(3)),
glwe_modular_std_dev: Some(StandardDev(0.0000000000039666089171633006)),
polynomial_size: Some(PolynomialSize(1 << 12)),
message_modulus: Some(1),
..Default::default()
}
),
),
(
"2_bits_prec".to_string(),
(
CryptoParametersRecord {
lwe_dimension: Some(LweDimension(550)),
ks_base_log: Some(DecompositionBaseLog(5)),
ks_level: Some(DecompositionLevelCount(10)),
glwe_dimension: Some(GlweDimension(3)),
glwe_modular_std_dev: Some(StandardDev(0.0000000000039666089171633006)),
polynomial_size: Some(PolynomialSize(1 << 11)),
message_modulus: Some(1),
..Default::default()
}
),
),
(
"3_bits_prec".to_string(),
(
CryptoParametersRecord {
lwe_dimension: Some(LweDimension(550)),
ks_base_log: Some(DecompositionBaseLog(8)),
ks_level: Some(DecompositionLevelCount(8)),
glwe_dimension: Some(GlweDimension(3)),
glwe_modular_std_dev: Some(StandardDev(0.0000000000039666089171633006)),
polynomial_size: Some(PolynomialSize(1 << 12)),
message_modulus: Some(1),
..Default::default()
}
),
),
(
"4_bits_multi_bit_group_2".to_string(),
(
CryptoParametersRecord {
lwe_dimension: Some(LweDimension(550)),
ks_base_log: Some(DecompositionBaseLog(12)),
ks_level: Some(DecompositionLevelCount(4)),
glwe_dimension: Some(GlweDimension(3)),
glwe_modular_std_dev: Some(StandardDev(0.0000000000000003152931493498455)),
polynomial_size: Some(PolynomialSize(1 << 11)),
message_modulus: Some(2),
..Default::default()
}
),
),
]
} else {
// For now there are no parameters available to test multi bit PBS on 32 bits.
vec![]
}
}
fn tensor_product_with_relin<Scalar: UnsignedTorus + CastInto<usize>>(c: &mut Criterion)
{
//only written for local development benchmarking
let bench_name = "leveled_mult_and_relin";
let mut bench_group = c.benchmark_group(bench_name);
// Create the PRNG
let mut seeder = new_seeder();
let seeder = seeder.as_mut();
let mut encryption_generator =
EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
let mut secret_generator =
SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
let ciphertext_modulus: tfhe::core_crypto::prelude::CiphertextModulus<Scalar> = tfhe::core_crypto::prelude::CiphertextModulus::new_native();
for (name, params) in packed_CJP_optimized::<Scalar>().iter()
{
// Create the GlweSecretKey
let glwe_secret_key: GlweSecretKeyOwned<Scalar> = allocate_and_generate_new_binary_glwe_secret_key(
params.glwe_dimension.unwrap(),
params.polynomial_size.unwrap(),
&mut secret_generator,
);
let glwe_relin_key = allocate_and_generate_glwe_relinearisation_key(
&glwe_secret_key,
params.ks_base_log.unwrap(),
params.ks_level.unwrap(),
params.glwe_modular_std_dev.unwrap(),
ciphertext_modulus,
&mut encryption_generator,
);
let log_delta1 = 59;
let log_delta2 = 60;
let log_delta = std::cmp::min(log_delta1, log_delta2);
//let output_log_delta = log_delta1 + log_delta2 - log_delta;
// Allocate a new GlweCiphertext and encrypt our plaintext
let mut glwe_1 = GlweCiphertext::new(Scalar::ZERO,
params.glwe_dimension.unwrap().to_glwe_size(),
params.polynomial_size.unwrap(),
ciphertext_modulus);
encrypt_glwe_ciphertext_assign(&glwe_secret_key,
& mut glwe_1,params.glwe_modular_std_dev.unwrap(),
&mut encryption_generator,);
let mut glwe_2 = GlweCiphertext::new(Scalar::ZERO,
params.glwe_dimension.unwrap().to_glwe_size(),
params.polynomial_size.unwrap(),
ciphertext_modulus);
encrypt_glwe_ciphertext_assign(&glwe_secret_key,
& mut glwe_2,params.glwe_modular_std_dev.unwrap(),
&mut encryption_generator,);
// Perform the tensor product
let scale =Scalar::ONE << log_delta;
//let tensor_output = glwe_tensor_product(&glwe_1, &glwe_2, scale);
let mut output_glwe_ciphertext =
GlweCiphertext::new(Scalar::ZERO, params.glwe_dimension.unwrap().to_glwe_size(), params.polynomial_size.unwrap(), ciphertext_modulus);
//glwe_relinearisation(&tensor_output, &glwe_relin_key, &mut output_glwe_ciphertext);
let id = format!("{bench_name}_{name}");
{
bench_group.bench_function(&id, |b| {
b.iter(|| {
tensor_mult_with_relin(
&glwe_1,
&glwe_2,
scale,
&glwe_relin_key,
& mut output_glwe_ciphertext,
);
black_box(&mut output_glwe_ciphertext);
})
});
}
let bit_size = (params.message_modulus.unwrap_or(2) as u64).ilog2();
write_to_json(
&id,
*params,
name,
"lev mult",
&OperatorType::Atomic,
bit_size,
vec![bit_size],
);
}
}
//TODO check parameters
fn packed_tensor_prod_optimized<Scalar: Numeric>(
) -> Vec<(String, CryptoParametersRecord)> {
if Scalar::BITS == 64 {
vec![
/*(
"1_bits_prec".to_string(),
(
CryptoParametersRecord {
lwe_dimension: Some(LweDimension(2048)),
lwe_modular_std_dev: Some(StandardDev(0.000000012752307213087621)),
ks_base_log: Some(DecompositionBaseLog(8)),
ks_level: Some(DecompositionLevelCount(8)),
glwe_dimension: Some(GlweDimension(1)),
glwe_modular_std_dev: Some(StandardDev(0.0000000000039666089171633006)),
polynomial_size: Some(PolynomialSize(1 << 12)),
message_modulus: Some(2),
pbs_base_log: Some(DecompositionBaseLog(8)),
pbs_level: Some(DecompositionLevelCount(8)),
..Default::default()
}
),
),
(
"2_bits_prec".to_string(),
(
CryptoParametersRecord {
lwe_dimension: Some(LweDimension(6144)),
ks_base_log: Some(DecompositionBaseLog(5)),
ks_level: Some(DecompositionLevelCount(10)),
glwe_dimension: Some(GlweDimension(3)),
glwe_modular_std_dev: Some(StandardDev(0.0000000000039666089171633006)),
polynomial_size: Some(PolynomialSize(1 << 11)),
message_modulus: Some(1),
..Default::default()
}
),
),
(
"3_bits_prec".to_string(),
(
CryptoParametersRecord {
lwe_dimension: Some(LweDimension(12288)),
ks_base_log: Some(DecompositionBaseLog(8)),
ks_level: Some(DecompositionLevelCount(8)),
glwe_dimension: Some(GlweDimension(3)),
glwe_modular_std_dev: Some(StandardDev(0.0000000000039666089171633006)),
polynomial_size: Some(PolynomialSize(1 << 12)),
message_modulus: Some(1),
..Default::default()
}
),
),
(
"4_bits_prec".to_string(),
(
CryptoParametersRecord {
lwe_dimension: Some(LweDimension(6144)),
ks_base_log: Some(DecompositionBaseLog(12)),
ks_level: Some(DecompositionLevelCount(4)),
glwe_dimension: Some(GlweDimension(3)),
glwe_modular_std_dev: Some(StandardDev(0.0000000000000003152931493498455)),
polynomial_size: Some(PolynomialSize(1 << 11)),
message_modulus: Some(2),
..Default::default()
}
),
),
(
"8_bits_prec".to_string(),
(
CryptoParametersRecord {
lwe_dimension: Some(LweDimension(2048)),
lwe_modular_std_dev: Some(StandardDev(0.000000012752307213087621)),
ks_base_log: Some(DecompositionBaseLog(20)),
ks_level: Some(DecompositionLevelCount(2)),
glwe_dimension: Some(GlweDimension(1)),
glwe_modular_std_dev: Some(StandardDev(0.0000000000000003152931493498455)),
polynomial_size: Some(PolynomialSize(1 << 11)),
message_modulus: Some(2^8),
pbs_base_log: Some(DecompositionBaseLog(20)),
pbs_level: Some(DecompositionLevelCount(2)),
..Default::default()
}
),
),*/
(
"6_bits_prec".to_string(),
(
CryptoParametersRecord {
lwe_dimension: Some(LweDimension(2048)),
lwe_modular_std_dev: Some(StandardDev(0.000000012752307213087621)),
ks_base_log: Some(DecompositionBaseLog(8)),
ks_level: Some(DecompositionLevelCount(8)),
glwe_dimension: Some(GlweDimension(1)),
glwe_modular_std_dev: Some(StandardDev(0.0000000000000003152931493498455)),
polynomial_size: Some(PolynomialSize(1 << 12)),
message_modulus: Some(2^6),
pbs_base_log: Some(DecompositionBaseLog(8)),
pbs_level: Some(DecompositionLevelCount(8)),
..Default::default()
}
),
),
(
"7_bits_prec".to_string(),
(
CryptoParametersRecord {
lwe_dimension: Some(LweDimension(2048)),
lwe_modular_std_dev: Some(StandardDev(0.000000012752307213087621)),
ks_base_log: Some(DecompositionBaseLog(8)),
ks_level: Some(DecompositionLevelCount(8)),
glwe_dimension: Some(GlweDimension(1)),
glwe_modular_std_dev: Some(StandardDev(0.0000000000000003152931493498455)),
polynomial_size: Some(PolynomialSize(1 << 12)),
message_modulus: Some(2^7),
pbs_base_log: Some(DecompositionBaseLog(8)),
pbs_level: Some(DecompositionLevelCount(8)),
..Default::default()
}
),
),
]
} else {
// For now there are no parameters available to test multi bit PBS on 32 bits.
vec![]
}
}
fn packed_CJP_optimized<Scalar: Numeric>(
) -> Vec<(String, CryptoParametersRecord)> {
if Scalar::BITS == 64 {
vec![
(
"1_bits_prec".to_string(),
(
CryptoParametersRecord {
lwe_dimension: Some(LweDimension(588)),
lwe_modular_std_dev: Some(StandardDev(0.0001545113)),
ks_base_log: Some(DecompositionBaseLog(15)),
ks_level: Some(DecompositionLevelCount(1)),
glwe_dimension: Some(GlweDimension(5)),
glwe_modular_std_dev: Some(StandardDev(0.000000000044360664)),
polynomial_size: Some(PolynomialSize(1 <<8)),
message_modulus: Some(2),
pbs_base_log: Some(DecompositionBaseLog(15)),
pbs_level: Some(DecompositionLevelCount(1)),
..Default::default()
}
),
),
(
"6_bits_prec".to_string(),
(
CryptoParametersRecord {
lwe_dimension: Some(LweDimension(840)),
lwe_modular_std_dev: Some(StandardDev(0.00000148613)),
ks_base_log: Some(DecompositionBaseLog(14)),
ks_level: Some(DecompositionLevelCount(2)),
glwe_dimension: Some(GlweDimension(1)),
glwe_modular_std_dev: Some(StandardDev(0.00000000000000000021684043)),
polynomial_size: Some(PolynomialSize(1 << 12)),
message_modulus: Some(2^6),
pbs_base_log: Some(DecompositionBaseLog(14)),
pbs_level: Some(DecompositionLevelCount(2)),
..Default::default()
}
),
),
(
"7_bits_prec".to_string(),
(
CryptoParametersRecord {
lwe_dimension: Some(LweDimension(896)),
lwe_modular_std_dev: Some(StandardDev(0.000000529083954)),
ks_base_log: Some(DecompositionBaseLog(15)),
ks_level: Some(DecompositionLevelCount(2)),
glwe_dimension: Some(GlweDimension(1)),
glwe_modular_std_dev: Some(StandardDev(0.00000000000000000021684043)),
polynomial_size: Some(PolynomialSize(1 << 13)),
message_modulus: Some(2^7),
pbs_base_log: Some(DecompositionBaseLog(15)),
pbs_level: Some(DecompositionLevelCount(2)),
..Default::default()
}
),
),
(
"8_bits_prec".to_string(),
(
CryptoParametersRecord {
lwe_dimension: Some(LweDimension(968)),
lwe_modular_std_dev: Some(StandardDev(0.000000139812821)),
ks_base_log: Some(DecompositionBaseLog(11)),
ks_level: Some(DecompositionLevelCount(3)),
glwe_dimension: Some(GlweDimension(1)),
glwe_modular_std_dev: Some(StandardDev(0.00000000000000000021684043)),
polynomial_size: Some(PolynomialSize(1 << 14)),
message_modulus: Some(2^8),
pbs_base_log: Some(DecompositionBaseLog(11)),
pbs_level: Some(DecompositionLevelCount(3)),
..Default::default()
}
),
),
]
} else {
// For now there are no parameters available to test multi bit PBS on 32 bits.
vec![]
}
}
fn public_funct_ks<Scalar: UnsignedTorus + CastInto<usize>>(c: &mut Criterion)
{
//only written for local development benchmarking
let bench_name = "public_funct_ks";
let mut bench_group = c.benchmark_group(bench_name);
// Create the PRNG
let mut seeder = new_seeder();
let seeder = seeder.as_mut();
let mut encryption_generator =
EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
let mut secret_generator =
SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
let ciphertext_modulus: tfhe::core_crypto::prelude::CiphertextModulus<Scalar> = tfhe::core_crypto::prelude::CiphertextModulus::new_native();
for (name, params) in packed_CJP_optimized::<Scalar>().iter()
{
// Create the LweSecretKey
let lwe_secret_key = allocate_and_generate_new_binary_lwe_secret_key(
params.lwe_dimension.unwrap(),
&mut secret_generator,
);
// Create the GlweSecretKey
let glwe_secret_key: GlweSecretKeyOwned<Scalar> = allocate_and_generate_new_binary_glwe_secret_key(
params.glwe_dimension.unwrap(),
params.polynomial_size.unwrap(),
&mut secret_generator,
);
let mut lwe_pubfpksk = LwePublicFunctionalPackingKeyswitchKey::new(
Scalar::ZERO,
params.ks_base_log.unwrap(),
params.ks_level.unwrap(),
params.lwe_dimension.unwrap(),
params.glwe_dimension.unwrap().to_glwe_size(),
params.polynomial_size.unwrap(),
ciphertext_modulus,
);
generate_lwe_public_functional_packing_keyswitch_key(
&lwe_secret_key,
&glwe_secret_key,
&mut lwe_pubfpksk,
params.glwe_modular_std_dev.unwrap(),
&mut encryption_generator,
);
let lwe_ciphertext_count = LweCiphertextCount(20);
let lwe_plaintext_list = PlaintextList::new(Scalar::ONE << 59, PlaintextCount(20));
let mut lwe_list_1 = LweCiphertextList::new(
Scalar::ZERO,
params.lwe_dimension.unwrap().to_lwe_size(),
lwe_ciphertext_count,
ciphertext_modulus,
);
encrypt_lwe_ciphertext_list(
&lwe_secret_key,
&mut lwe_list_1,
&lwe_plaintext_list,
params.glwe_modular_std_dev.unwrap(),
&mut encryption_generator,
);
let mut output_glwe_ciphertext =
GlweCiphertext::new(Scalar::ZERO,
params.glwe_dimension.unwrap().to_glwe_size(),
params.polynomial_size.unwrap(),
ciphertext_modulus);
let id = format!("{bench_name}_{name}");
{
bench_group.bench_function(&id, |b| {
b.iter(|| {
public_functional_keyswitch_lwe_ciphertexts_into_glwe_ciphertext(
&lwe_pubfpksk,
&mut output_glwe_ciphertext,
&lwe_list_1,
| x: Vec<Scalar>| {
let mut packed1: Vec<Scalar> = vec![Scalar::ZERO;lwe_pubfpksk.output_polynomial_size().0];
x.iter().enumerate().for_each(|(iter,y)| packed1[iter] = *y);
Polynomial::from_container(packed1)
}
);
black_box(&mut output_glwe_ciphertext);
})
});
}
let bit_size = (params.message_modulus.unwrap_or(2) as u64).ilog2();
write_to_json(
&id,
*params,
name,
"public functional ks",
&OperatorType::Atomic,
bit_size,
vec![bit_size],
);
}
}
fn packed_mul<Scalar: UnsignedTorus + CastInto<usize>>(c: &mut Criterion)
{
//only written for local development benchmarking
let bench_name = "packed_multiplication";
let mut bench_group = c.benchmark_group(bench_name);
// Create the PRNG
let mut seeder = new_seeder();
let seeder = seeder.as_mut();
let mut encryption_generator =
EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
let mut secret_generator =
SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
let ciphertext_modulus: tfhe::core_crypto::prelude::CiphertextModulus<Scalar> = tfhe::core_crypto::prelude::CiphertextModulus::new_native();
for (name, params) in packed_tensor_prod_optimized::<Scalar>().iter()
{
// Create the LweSecretKey
let lwe_secret_key = allocate_and_generate_new_binary_lwe_secret_key(
params.lwe_dimension.unwrap(),
&mut secret_generator,
);
// Create the GlweSecretKey
let glwe_secret_key: GlweSecretKeyOwned<Scalar> = allocate_and_generate_new_binary_glwe_secret_key(
params.glwe_dimension.unwrap(),
params.polynomial_size.unwrap(),
&mut secret_generator,
);
let glwe_relin_key = allocate_and_generate_glwe_relinearisation_key(
&glwe_secret_key,
params.ks_base_log.unwrap(),
params.ks_level.unwrap(),
params.glwe_modular_std_dev.unwrap(),
ciphertext_modulus,
&mut encryption_generator,
);
let mut lwe_pubfpksk = LwePublicFunctionalPackingKeyswitchKey::new(
Scalar::ZERO,
params.ks_base_log.unwrap(),
params.ks_level.unwrap(),
params.lwe_dimension.unwrap(),
params.glwe_dimension.unwrap().to_glwe_size(),
params.polynomial_size.unwrap(),
ciphertext_modulus,
);
generate_lwe_public_functional_packing_keyswitch_key(
&lwe_secret_key,
&glwe_secret_key,
&mut lwe_pubfpksk,
params.glwe_modular_std_dev.unwrap(),
&mut encryption_generator,
);
let log_delta1 = 59;
let log_delta2 = 60;
let log_delta = std::cmp::min(log_delta1, log_delta2);
let lwe_ciphertext_count = LweCiphertextCount(20);
let lwe_plaintext_list = PlaintextList::new(Scalar::ONE << 59, PlaintextCount(20));
let mut lwe_list_1 = LweCiphertextList::new(
Scalar::ZERO,
params.lwe_dimension.unwrap().to_lwe_size(),
lwe_ciphertext_count,
ciphertext_modulus,
);
encrypt_lwe_ciphertext_list(
&lwe_secret_key,
&mut lwe_list_1,
&lwe_plaintext_list,
params.glwe_modular_std_dev.unwrap(),
&mut encryption_generator,
);
let mut lwe_list_2 = LweCiphertextList::new(
Scalar::ZERO,
params.lwe_dimension.unwrap().to_lwe_size(),
lwe_ciphertext_count,
ciphertext_modulus,
);
encrypt_lwe_ciphertext_list(
&lwe_secret_key,
&mut lwe_list_2,
&lwe_plaintext_list,
params.glwe_modular_std_dev.unwrap(),
&mut encryption_generator,
);
// Perform the tensor product
let scale =Scalar::ONE << log_delta;
let mut output_lwe_list =
LweCiphertextList::new(
Scalar::ZERO,
//fix this LWE dimension to be k*N
params.lwe_dimension.unwrap().to_lwe_size(),
lwe_ciphertext_count,
ciphertext_modulus,
);
let id = format!("{bench_name}_{name}");
{
bench_group.bench_function(&id, |b| {
b.iter(|| {
packed_mult(
&lwe_list_1,
&lwe_list_2,
&lwe_pubfpksk,
&glwe_relin_key,
scale,
&mut output_lwe_list,
);
black_box(&mut output_lwe_list);
})
});
}
let bit_size = (params.message_modulus.unwrap_or(2) as u64).ilog2();
write_to_json(
&id,
*params,
name,
"packed mult",
&OperatorType::Atomic,
bit_size,
vec![bit_size],
);
}
}
fn packed_sum_prod<Scalar: UnsignedTorus + CastInto<usize>>(c: &mut Criterion)
{
//only written for local development benchmarking
let bench_name = "sum_of_products";
let mut bench_group = c.benchmark_group(bench_name);
// Create the PRNG
let mut seeder = new_seeder();
let seeder = seeder.as_mut();
let mut encryption_generator =
EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
let mut secret_generator =
SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
let ciphertext_modulus: tfhe::core_crypto::prelude::CiphertextModulus<Scalar> = tfhe::core_crypto::prelude::CiphertextModulus::new_native();
for (name, params) in packed_tensor_prod_optimized::<Scalar>().iter()
{
// Create the LweSecretKey
let lwe_secret_key = allocate_and_generate_new_binary_lwe_secret_key(
params.lwe_dimension.unwrap(),
&mut secret_generator,
);
// Create the GlweSecretKey
let glwe_secret_key: GlweSecretKeyOwned<Scalar> = allocate_and_generate_new_binary_glwe_secret_key(
params.glwe_dimension.unwrap(),
params.polynomial_size.unwrap(),
&mut secret_generator,
);
let glwe_relin_key = allocate_and_generate_glwe_relinearisation_key(
&glwe_secret_key,
params.ks_base_log.unwrap(),
params.ks_level.unwrap(),
params.glwe_modular_std_dev.unwrap(),
ciphertext_modulus,
&mut encryption_generator,
);
let mut lwe_pubfpksk = LwePublicFunctionalPackingKeyswitchKey::new(
Scalar::ZERO,
params.ks_base_log.unwrap(),
params.ks_level.unwrap(),
params.lwe_dimension.unwrap(),
params.glwe_dimension.unwrap().to_glwe_size(),
params.polynomial_size.unwrap(),
ciphertext_modulus,
);
let mut encryption_generator =
EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
generate_lwe_public_functional_packing_keyswitch_key(
&lwe_secret_key,
&glwe_secret_key,
&mut lwe_pubfpksk,
params.glwe_modular_std_dev.unwrap(),
&mut encryption_generator,
);
let lwe_ciphertext_count= LweCiphertextCount(20);
let lwe_plaintext_list = PlaintextList::new(Scalar::ONE << 59, PlaintextCount(20));
let mut lwe_list_1 = LweCiphertextList::new(
Scalar::ZERO,
params.lwe_dimension.unwrap().to_lwe_size(),
lwe_ciphertext_count,
ciphertext_modulus,
);
encrypt_lwe_ciphertext_list(
&lwe_secret_key,
&mut lwe_list_1,
&lwe_plaintext_list,
params.glwe_modular_std_dev.unwrap(),
&mut encryption_generator,
);
let mut lwe_list_2 = LweCiphertextList::new(
Scalar::ZERO,
params.lwe_dimension.unwrap().to_lwe_size(),
lwe_ciphertext_count,
ciphertext_modulus,
);
encrypt_lwe_ciphertext_list(
&lwe_secret_key,
&mut lwe_list_2,
&lwe_plaintext_list,
params.glwe_modular_std_dev.unwrap(),
&mut encryption_generator,
);
let log_delta1 = 59;
let log_delta2 = 60;
let log_delta = std::cmp::min(log_delta1, log_delta2);
// Perform the tensor product
let scale =Scalar::ONE << log_delta;
let mut output_lwe_ciphertext = LweCiphertext::new(
Scalar::ZERO,
params.lwe_dimension.unwrap().to_lwe_size(),
ciphertext_modulus,
);
let id = format!("{bench_name}_{name}");
{
bench_group.bench_function(&id, |b| {
b.iter(|| {
packed_sum_product(
&lwe_list_1,
&lwe_list_2,
&lwe_pubfpksk,
&glwe_relin_key,
scale,
&mut output_lwe_ciphertext
);
black_box(&mut output_lwe_ciphertext);
})
});
}
let bit_size = (params.message_modulus.unwrap_or(2) as u64).ilog2();
write_to_json(
&id,
*params,
name,
"sum of products",
&OperatorType::Atomic,
bit_size,
vec![bit_size],
);
}
}

View File

@@ -0,0 +1,189 @@
//! Module containing primitives pertaining to [`GLWE ciphertext
//! keyswitch`](`GlweKeyswitchKey#glwe-keyswitch`).
use crate::core_crypto::algorithms::polynomial_algorithms::*;
use crate::core_crypto::commons::math::decomposition::SignedDecomposer;
use crate::core_crypto::commons::numeric::UnsignedInteger;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
/// Keyswitch an [`GLWE ciphertext`](`GlweCiphertext`) encrytped under an
/// [`GLWE secret key`](`GlweSecretKey`) to another [`GLWE secret key`](`GlweSecretKey`).
///
/// # Formal Definition
///
/// See [`GLWE keyswitch key`](`GlweKeyswitchKey#glwe-keyswitch`).
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::prelude::*;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for LweKeyswitchKey creation
/// let input_glwe_dimension = GlweDimension(2);
/// let poly_size = PolynomialSize(512);
/// let glwe_modular_std_dev = StandardDev(0.000007069849454709433);
/// let output_glwe_dimension = GlweDimension(1);
/// let decomp_base_log = DecompositionBaseLog(3);
/// let decomp_level_count = DecompositionLevelCount(5);
/// let ciphertext_modulus = CiphertextModulus::new_native();
///
/// // Create the PRNG
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut encryption_generator =
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
///
/// // Create the LweSecretKey
/// let input_glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
/// input_glwe_dimension,
/// poly_size,
/// &mut secret_generator,
/// );
/// let output_glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
/// output_glwe_dimension,
/// poly_size,
/// &mut secret_generator,
/// );
///
/// let ksk = allocate_and_generate_new_glwe_keyswitch_key(
/// &input_glwe_secret_key,
/// &output_glwe_secret_key,
/// decomp_base_log,
/// decomp_level_count,
/// glwe_modular_std_dev,
/// ciphertext_modulus,
/// &mut encryption_generator,
/// );
///
/// // Create the plaintext
/// let msg = 3u64;
/// let plaintext_list = PlaintextList::new(msg << 60, PlaintextCount(poly_size.0));
///
/// // Create a new GlweCiphertext
/// let mut input_glwe = GlweCiphertext::new(
/// 0u64,
/// input_glwe_dimension.to_glwe_size(),
/// poly_size,
/// ciphertext_modulus,
/// );
///
/// encrypt_glwe_ciphertext(
/// &input_glwe_secret_key,
/// &mut input_glwe,
/// &plaintext_list,
/// glwe_modular_std_dev,
/// &mut encryption_generator,
/// );
///
/// let mut output_glwe = GlweCiphertext::new(
/// 0u64,
/// output_glwe_secret_key.glwe_dimension().to_glwe_size(),
/// output_glwe_secret_key.polynomial_size(),
/// ciphertext_modulus,
/// );
///
/// keyswitch_glwe_ciphertext(&ksk, &mut input_glwe, &mut output_glwe);
///
/// let mut output_plaintext_list = PlaintextList::new(0u64, plaintext_list.plaintext_count());
///
/// let decrypted_plaintext = decrypt_glwe_ciphertext(
/// &output_glwe_secret_key,
/// &output_glwe,
/// &mut output_plaintext_list,
/// );
///
/// // Round and remove encoding
/// // First create a decomposer working on the high 4 bits corresponding to our encoding.
/// let decomposer = SignedDecomposer::new(DecompositionBaseLog(4), DecompositionLevelCount(1));
///
/// output_plaintext_list
/// .iter_mut()
/// .for_each(|elt| *elt.0 = decomposer.closest_representable(*elt.0));
///
/// // Get the raw vector
/// let mut cleartext_list = output_plaintext_list.into_container();
/// // Remove the encoding
/// cleartext_list.iter_mut().for_each(|elt| *elt = *elt >> 60);
/// // Get the list immutably
/// let cleartext_list = cleartext_list;
///
/// // Check we recovered the original message for each plaintext we encrypted
/// cleartext_list.iter().for_each(|&elt| assert_eq!(elt, msg));
/// ```
pub fn keyswitch_glwe_ciphertext<Scalar, KSKCont, InputCont, OutputCont>(
glwe_keyswitch_key: &GlweKeyswitchKey<KSKCont>,
input_glwe_ciphertext: &mut GlweCiphertext<InputCont>,
output_glwe_ciphertext: &mut GlweCiphertext<OutputCont>,
) where
Scalar: UnsignedInteger,
KSKCont: Container<Element = Scalar>,
InputCont: ContainerMut<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
{
assert!(
glwe_keyswitch_key.input_key_glwe_dimension()
== input_glwe_ciphertext.glwe_size().to_glwe_dimension(),
"Mismatched input GlweDimension. \
GlweKeyswitchKey input GlweDimension: {:?}, input GlweCiphertext GlweDimension {:?}.",
glwe_keyswitch_key.input_key_glwe_dimension(),
input_glwe_ciphertext.glwe_size().to_glwe_dimension(),
);
assert!(
glwe_keyswitch_key.output_key_glwe_dimension()
== output_glwe_ciphertext.glwe_size().to_glwe_dimension(),
"Mismatched output GlweDimension. \
GlweKeyswitchKey output GlweDimension: {:?}, output GlweCiphertext GlweDimension {:?}.",
glwe_keyswitch_key.output_key_glwe_dimension(),
output_glwe_ciphertext.glwe_size().to_glwe_dimension(),
);
assert!(
glwe_keyswitch_key.polynomial_size() == input_glwe_ciphertext.polynomial_size(),
"Mismatched input PolynomialSize. \
GlweKeyswithcKey input PolynomialSize: {:?}, input GlweCiphertext PolynomialSize {:?}.",
glwe_keyswitch_key.polynomial_size(),
input_glwe_ciphertext.polynomial_size(),
);
assert!(
glwe_keyswitch_key.polynomial_size() == output_glwe_ciphertext.polynomial_size(),
"Mismatched output PolynomialSize. \
GlweKeyswitchKey output PolynomialSize: {:?}, output GlweCiphertext PolynomialSize {:?}.",
glwe_keyswitch_key.polynomial_size(),
output_glwe_ciphertext.polynomial_size(),
);
// Clear the output ciphertext, as it will get updated gradually
output_glwe_ciphertext.as_mut().fill(Scalar::ZERO);
// Copy the input body to the output ciphertext
polynomial_wrapping_add_assign(
&mut output_glwe_ciphertext.get_mut_body().as_mut_polynomial(),
&input_glwe_ciphertext.get_body().as_polynomial(),
);
// We instantiate a decomposer
let decomposer = SignedDecomposer::new(
glwe_keyswitch_key.decomposition_base_log(),
glwe_keyswitch_key.decomposition_level_count(),
);
for (keyswitch_key_block, input_mask_element) in glwe_keyswitch_key
.iter()
.zip(input_glwe_ciphertext.get_mask().as_polynomial_list().iter())
{
let mut decomposition_iter = decomposer.decompose_slice(input_mask_element.as_ref());
// loop over the number of levels in reverse (from highest to lowest)
for level_key_ciphertext in keyswitch_key_block.iter().rev() {
let decomposed = decomposition_iter.next_term().unwrap();
polynomial_list_wrapping_sub_scalar_mul_assign(
&mut output_glwe_ciphertext.as_mut_polynomial_list(),
&level_key_ciphertext.as_polynomial_list(),
&Polynomial::from_container(decomposed.as_slice()),
);
}
}
}

View File

@@ -0,0 +1,207 @@
//! Module containing primitives pertaining to [`GLWE keyswitch key generation`](`GlweKeyswitchKey`)
use crate::core_crypto::algorithms::*;
use crate::core_crypto::commons::dispersion::DispersionParameter;
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
use crate::core_crypto::commons::math::decomposition::{DecompositionLevel, DecompositionTerm};
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
/// Fill a [`GLWE keyswitch key`](`GlweKeyswitchKey`) with an actual keyswitching key constructed
/// from an input and an output key [`GLWE secret key`](`GlweSecretKey`).
///
/// ```
/// use tfhe::core_crypto::prelude::*;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for LweKeyswitchKey creation
/// let input_glwe_dimension = GlweDimension(2);
/// let polynomial_size = PolynomialSize(1024);
/// let glwe_modular_std_dev = StandardDev(0.000007069849454709433);
/// let output_glwe_dimension = GlweDimension(1);
/// let decomp_base_log = DecompositionBaseLog(3);
/// let decomp_level_count = DecompositionLevelCount(5);
/// let ciphertext_modulus = CiphertextModulus::new_native();
///
/// // Create the PRNG
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut encryption_generator =
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
///
/// // Create the GlweSecretKey
/// let input_glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
/// input_glwe_dimension,
/// polynomial_size,
/// &mut secret_generator,
/// );
/// let output_glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
/// output_glwe_dimension,
/// polynomial_size,
/// &mut secret_generator,
/// );
///
/// let mut ksk = GlweKeyswitchKey::new(
/// 0u64,
/// decomp_base_log,
/// decomp_level_count,
/// input_glwe_dimension,
/// output_glwe_dimension,
/// polynomial_size,
/// ciphertext_modulus,
/// );
///
/// generate_glwe_keyswitch_key(
/// &input_glwe_secret_key,
/// &output_glwe_secret_key,
/// &mut ksk,
/// glwe_modular_std_dev,
/// &mut encryption_generator,
/// );
///
/// assert!(ksk.as_ref().iter().all(|&x| x == 0) == false);
/// ```
pub fn generate_glwe_keyswitch_key<Scalar, InputKeyCont, OutputKeyCont, KSKeyCont, Gen>(
input_glwe_sk: &GlweSecretKey<InputKeyCont>,
output_glwe_sk: &GlweSecretKey<OutputKeyCont>,
glwe_keyswitch_key: &mut GlweKeyswitchKey<KSKeyCont>,
noise_parameters: impl DispersionParameter,
generator: &mut EncryptionRandomGenerator<Gen>,
) where
Scalar: UnsignedTorus,
InputKeyCont: Container<Element = Scalar>,
OutputKeyCont: Container<Element = Scalar>,
KSKeyCont: ContainerMut<Element = Scalar>,
Gen: ByteRandomGenerator,
{
assert!(
glwe_keyswitch_key.input_key_glwe_dimension() == input_glwe_sk.glwe_dimension(),
"The destination GlweKeyswitchKey input GlweDimension is not equal \
to the input GlweSecretKey GlweDimension. Destination: {:?}, input: {:?}",
glwe_keyswitch_key.input_key_glwe_dimension(),
input_glwe_sk.glwe_dimension()
);
assert!(
glwe_keyswitch_key.output_key_glwe_dimension() == output_glwe_sk.glwe_dimension(),
"The destination GlweKeyswitchKey output GlweDimension is not equal \
to the output GlweSecretKey GlweDimension. Destination: {:?}, output: {:?}",
glwe_keyswitch_key.output_key_glwe_dimension(),
input_glwe_sk.glwe_dimension()
);
assert!(
glwe_keyswitch_key.polynomial_size() == input_glwe_sk.polynomial_size(),
"The destination GlweKeyswitchKey input PolynomialSize is not equal \
to the input GlweSecretKey PolynomialSize. Destination: {:?}, input: {:?}",
glwe_keyswitch_key.polynomial_size(),
input_glwe_sk.polynomial_size(),
);
assert!(
glwe_keyswitch_key.polynomial_size() == output_glwe_sk.polynomial_size(),
"The distination GlweKeyswitchKey output PolynomialSize is not equal \
to the output GlweSecretKey PolynomialSize. Destination: {:?}, output: {:?}",
glwe_keyswitch_key.polynomial_size(),
output_glwe_sk.polynomial_size(),
);
let decomp_base_log = glwe_keyswitch_key.decomposition_base_log();
let decomp_level_count = glwe_keyswitch_key.decomposition_level_count();
/*
// forking the generator and using loop_generator works to generate glwe keyswitch keys but
// break lwe_trace_packing_keyswotch_key_generation
let gen_iter = generator
.fork_glweks_to_glweks_chunks::<Scalar>(
decomp_level_count,
input_glwe_sk.glwe_dimension(),
output_glwe_sk.glwe_dimension().to_glwe_size(),
input_glwe_sk.polynomial_size(),
)
.unwrap();
*/
// Iterate over the input key elements and the destination glwe_keyswitch_key memory
//for ((input_key_polynomial, mut keyswitch_key_block), mut _loop_generator) in input_glwe_sk
for (input_key_polynomial, mut keyswitch_key_block) in input_glwe_sk
.as_polynomial_list()
.iter()
.zip(glwe_keyswitch_key.iter_mut())
//.zip(gen_iter)
{
// The plaintexts used to encrypt a key element will be stored in this buffer
let mut decomposition_polynomials_buffer = PolynomialList::new(
Scalar::ZERO,
input_glwe_sk.polynomial_size(),
PolynomialCount(decomp_level_count.0),
);
// We fill the buffer with the powers of the key elmements
for (level, mut message_polynomial) in (1..=decomp_level_count.0)
.map(DecompositionLevel)
.zip(decomposition_polynomials_buffer.as_mut_view().iter_mut())
{
for (message, input_key_element) in message_polynomial
.iter_mut()
.zip(input_key_polynomial.iter())
{
*message = DecompositionTerm::new(level, decomp_base_log, *input_key_element)
.to_recomposition_summand();
}
}
let decomposition_plaintexts_buffer =
PlaintextList::from_container(decomposition_polynomials_buffer.into_container());
encrypt_glwe_ciphertext_list(
output_glwe_sk,
&mut keyswitch_key_block,
&decomposition_plaintexts_buffer,
noise_parameters,
//&mut loop_generator,
generator,
);
}
}
/// Allocate a new [`GLWE keyswitch key`](`GlweKeyswitchKey`) and fill it with an actual
/// keyswitching key constructed from an input and an output key
/// [`GLWE secret key`](`GlweSecretKey`).
///
/// See [`keyswitch_glwe_ciphertext`] for usage.
pub fn allocate_and_generate_new_glwe_keyswitch_key<Scalar, InputKeyCont, OutputKeyCont, Gen>(
input_glwe_sk: &GlweSecretKey<InputKeyCont>,
output_glwe_sk: &GlweSecretKey<OutputKeyCont>,
decomp_base_log: DecompositionBaseLog,
decomp_level_count: DecompositionLevelCount,
noise_parameters: impl DispersionParameter,
ciphertext_modulus: CiphertextModulus<Scalar>,
generator: &mut EncryptionRandomGenerator<Gen>,
) -> GlweKeyswitchKeyOwned<Scalar>
where
Scalar: UnsignedTorus,
InputKeyCont: Container<Element = Scalar>,
OutputKeyCont: Container<Element = Scalar>,
Gen: ByteRandomGenerator,
{
let mut new_glwe_keyswitch_key = GlweKeyswitchKeyOwned::new(
Scalar::ZERO,
decomp_base_log,
decomp_level_count,
input_glwe_sk.glwe_dimension(),
output_glwe_sk.glwe_dimension(),
output_glwe_sk.polynomial_size(),
ciphertext_modulus,
);
generate_glwe_keyswitch_key(
input_glwe_sk,
output_glwe_sk,
&mut new_glwe_keyswitch_key,
noise_parameters,
generator,
);
new_glwe_keyswitch_key
}

View File

@@ -0,0 +1,156 @@
//! Module containing primitives pertaining to [`GLWE relinearisation key
//! generation`](`GlweRelinearisationKey`).
use crate::core_crypto::algorithms::polynomial_algorithms::*;
use crate::core_crypto::algorithms::*;
use crate::core_crypto::commons::dispersion::DispersionParameter;
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
use crate::core_crypto::prelude::{DecompositionBaseLog, DecompositionLevelCount, PolynomialCount};
/// Fill a [`GLWE relinearisation key`](`GlweRelinearisationKey`)
/// with an actual key.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::prelude::*;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for GlweCiphertext creation
/// let glwe_size = GlweSize(3);
/// let polynomial_size = PolynomialSize(1024);
/// let decomp_base_log = DecompositionBaseLog(3);
/// let decomp_level_count = DecompositionLevelCount(7);
/// let glwe_modular_std_dev = StandardDev(0.00000000000000029403601535432533);
/// let ciphertext_modulus = CiphertextModulus::new_native();
///
/// // Create the PRNG
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut encryption_generator =
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
///
/// // Create the GlweSecretKey
/// let glwe_secret_key: GlweSecretKey<Vec<u64>> = allocate_and_generate_new_binary_glwe_secret_key(
/// glwe_size.to_glwe_dimension(),
/// polynomial_size,
/// &mut secret_generator,
/// );
///
/// allocate_and_generate_glwe_relinearisation_key(
/// &glwe_secret_key,
/// decomp_base_log,
/// decomp_level_count,
/// glwe_modular_std_dev,
/// ciphertext_modulus,
/// &mut encryption_generator,
/// );
/// ```
pub fn generate_glwe_relinearisation_key<Scalar, GlweKeyCont, RelinKeyCont, Gen>(
glwe_secret_key: &GlweSecretKey<GlweKeyCont>,
glwe_relinearisation_key: &mut GlweRelinearisationKey<RelinKeyCont>,
noise_parameters: impl DispersionParameter,
generator: &mut EncryptionRandomGenerator<Gen>,
) where
Scalar: UnsignedTorus,
GlweKeyCont: Container<Element = Scalar>,
RelinKeyCont: ContainerMut<Element = Scalar>,
Gen: ByteRandomGenerator,
{
assert_eq!(
glwe_secret_key.glwe_dimension(),
glwe_relinearisation_key.glwe_dimension()
);
assert_eq!(
glwe_secret_key.polynomial_size(),
glwe_relinearisation_key.polynomial_size()
);
// We retrieve decomposition arguments
let glwe_dimension = glwe_relinearisation_key.glwe_dimension();
let decomp_level_count = glwe_relinearisation_key.decomposition_level_count();
let decomp_base_log = glwe_relinearisation_key.decomposition_base_log();
let polynomial_size = glwe_relinearisation_key.polynomial_size();
let ciphertext_modulus = glwe_relinearisation_key.ciphertext_modulus();
// Construct the "glwe secret key" we want to keyswitch from, this is made up of the square
// and cross terms appearing when squaring glwe_secret_key
let mut input_sk_poly_list = PolynomialList::new(
Scalar::ZERO,
polynomial_size,
PolynomialCount(glwe_dimension.0 * (glwe_dimension.0 + 1) / 2),
);
let mut input_sk_poly_list_iter = input_sk_poly_list.iter_mut();
for i in 0..glwe_dimension.0 {
for j in 0..i + 1 {
let mut input_key_pol = input_sk_poly_list_iter.next().unwrap();
polynomial_wrapping_sub_mul_assign(
&mut input_key_pol,
&glwe_secret_key.as_polynomial_list().get(i),
&glwe_secret_key.as_polynomial_list().get(j),
);
}
}
let input_glwe_sk = GlweSecretKey::from_container(input_sk_poly_list.as_ref(), polynomial_size);
let mut glwe_ks_key = GlweKeyswitchKey::from_container(
glwe_relinearisation_key.as_mut(),
decomp_base_log,
decomp_level_count,
glwe_dimension.to_glwe_size(),
polynomial_size,
ciphertext_modulus,
);
generate_glwe_keyswitch_key(
&input_glwe_sk,
glwe_secret_key,
&mut glwe_ks_key,
noise_parameters,
generator,
);
}
pub fn allocate_and_generate_glwe_relinearisation_key<Scalar, KeyCont, Gen>(
glwe_secret_key: &GlweSecretKey<KeyCont>,
decomp_base_log: DecompositionBaseLog,
decomp_level_count: DecompositionLevelCount,
noise_parameters: impl DispersionParameter,
ciphertext_modulus: CiphertextModulus<Scalar>,
generator: &mut EncryptionRandomGenerator<Gen>,
) -> GlweRelinearisationKeyOwned<Scalar>
where
Scalar: UnsignedTorus,
KeyCont: Container<Element = Scalar>,
Gen: ByteRandomGenerator,
{
let mut glwe_relinearisation_key = GlweRelinearisationKeyOwned::new(
Scalar::ZERO,
decomp_base_log,
decomp_level_count,
glwe_secret_key.glwe_dimension().to_glwe_size(),
glwe_secret_key.polynomial_size(),
ciphertext_modulus,
);
generate_glwe_relinearisation_key(
glwe_secret_key,
&mut glwe_relinearisation_key,
noise_parameters,
generator,
);
glwe_relinearisation_key
}
/*
* Parallel variant of [`generate_glwe_relinearisation_key`]. You may want to use this
* variant for better key generation times.
*/

View File

@@ -0,0 +1,600 @@
use crate::core_crypto::algorithms::polynomial_algorithms::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
use crate::core_crypto::prelude::*;
/// Compute the tensor product of the left-hand side [`GLWE ciphertext`](`GlweCiphertext`) with the
/// right-hand side [`GLWE ciphertext`](`GlweCiphertext`)
/// writing the result in the output [`GlweCiphertext<Vec<Scalar>>`](`GlweCiphertext<Vec<Scalar>>`).
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::algorithms::polynomial_algorithms::*;
/// use tfhe::core_crypto::prelude::*;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for LweCiphertext creation
/// let glwe_size = GlweSize(3);
/// let polynomial_size = PolynomialSize(512);
/// let glwe_modular_std_dev = StandardDev(0.000000000000000000029403601535432533);
/// let decomp_base_log = DecompositionBaseLog(3);
/// let decomp_level_count = DecompositionLevelCount(7);
/// let ciphertext_modulus = CiphertextModulus::new_native();
///
/// let log_delta1 = 59;
/// let log_delta2 = 60;
/// let log_delta = std::cmp::min(log_delta1, log_delta2);
/// let output_log_delta = log_delta1 + log_delta2 - log_delta;
///
/// // Create the PRNG
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut encryption_generator =
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
///
/// // Create the GlweSecretKey
/// let glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
/// glwe_size.to_glwe_dimension(),
/// polynomial_size,
/// &mut secret_generator,
/// );
///
/// // Create the first plaintext, we encrypt a single integer rather than a general polynomial
/// let msg_1 = 3u64;
/// let encoded_msg_1 = msg_1 << log_delta1;
///
/// let mut plaintext_list_1 = PlaintextList::new(0u64, PlaintextCount(polynomial_size.0));
/// plaintext_list_1.as_mut()[0] = encoded_msg_1;
///
/// // Create the first GlweCiphertext
/// let mut glwe_1 = GlweCiphertext::new(0u64, glwe_size, polynomial_size, ciphertext_modulus);
///
/// encrypt_glwe_ciphertext(
/// &glwe_secret_key,
/// &mut glwe_1,
/// &plaintext_list_1,
/// glwe_modular_std_dev,
/// &mut encryption_generator,
/// );
///
/// // Create the second plaintext
/// let msg_2 = 2u64;
/// let encoded_msg_2 = msg_2 << log_delta2;
///
/// let mut plaintext_list_2 = PlaintextList::new(0u64, PlaintextCount(polynomial_size.0));
/// plaintext_list_2.as_mut()[0] = encoded_msg_2;
///
/// // Create the second GlweCiphertext
/// let mut glwe_2 = GlweCiphertext::new(0u64, glwe_size, polynomial_size, ciphertext_modulus);
///
/// encrypt_glwe_ciphertext(
/// &glwe_secret_key,
/// &mut glwe_2,
/// &plaintext_list_2,
/// glwe_modular_std_dev,
/// &mut encryption_generator,
/// );
///
/// // Perform the tensor product
/// let scale = 1u64 << log_delta;
/// let tensor_output = glwe_tensor_product(&glwe_1, &glwe_2, scale);
///
/// // Compute the tensor product key
/// let tensor_glwe_dim = GlweDimension((glwe_size.0 - 1) * (glwe_size.0 + 2) / 2);
/// let mut tensor_key_poly_list =
/// PolynomialList::new(0u64, polynomial_size, PolynomialCount(tensor_glwe_dim.0));
/// let mut key_iter = tensor_key_poly_list.iter_mut();
///
/// for i in 0..glwe_size.0 - 1 {
/// for j in 0..i + 1 {
/// let mut key_pol = key_iter.next().unwrap();
/// polynomial_wrapping_sub_mul_assign(
/// &mut key_pol,
/// &glwe_secret_key.as_polynomial_list().get(i),
/// &glwe_secret_key.as_polynomial_list().get(j),
/// );
/// }
/// let mut key_pol = key_iter.next().unwrap();
/// polynomial_wrapping_add_assign(&mut key_pol, &glwe_secret_key.as_polynomial_list().get(i));
/// }
///
/// let tensor_key = GlweSecretKey::from_container(tensor_key_poly_list.as_ref(), polynomial_size);
///
/// // Decrypt the tensor product ciphertext
/// let mut output_plaintext = PlaintextList::new(0u64, PlaintextCount(polynomial_size.0));
///
/// // First create a decomposer working on the high 4 bits corresponding to our encoding.
/// let decomposer = SignedDecomposer::new(DecompositionBaseLog(2), DecompositionLevelCount(4));
///
/// decrypt_glwe_ciphertext(&tensor_key, &tensor_output, &mut output_plaintext);
/// output_plaintext
/// .iter_mut()
/// .for_each(|elt| *elt.0 = decomposer.closest_representable(*elt.0));
///
/// // Get the raw vector
/// let mut cleartext = output_plaintext.into_container();
/// // Remove the encoding
/// cleartext
/// .iter_mut()
/// .for_each(|elt| *elt = *elt >> output_log_delta);
/// // Get the list immutably
/// let cleartext = cleartext;
///
/// // Compute what the product should be
/// let pt1 = Polynomial::from_container(
/// plaintext_list_1
/// .into_container()
/// .iter()
/// .map(|&x| <u64 as CastInto<u128>>::cast_into(x))
/// .collect::<Vec<_>>(),
/// );
/// let pt2 = Polynomial::from_container(
/// plaintext_list_2
/// .into_container()
/// .iter()
/// .map(|&x| <u64 as CastInto<u128>>::cast_into(x))
/// .collect::<Vec<_>>(),
/// );
///
/// let mut product = Polynomial::new(0u128, polynomial_size);
/// polynomial_wrapping_mul(&mut product, &pt1, &pt2);
///
/// let mut scaled_product = Polynomial::new(0u64, polynomial_size);
/// scaled_product
/// .as_mut()
/// .iter_mut()
/// .zip(product.as_ref().iter())
/// .for_each(|(dest, &source)| {
/// *dest = u64::cast_from(source / <u64 as CastInto<u128>>::cast_into(scale))
/// >> output_log_delta
/// });
///
/// // Check we recovered the correct message
/// cleartext
/// .iter()
/// .zip(scaled_product.iter())
/// .for_each(|(&elt, coeff)| assert_eq!(elt, *coeff));
///
/// let glwe_relin_key = allocate_and_generate_glwe_relinearisation_key(
/// &glwe_secret_key,
/// decomp_base_log,
/// decomp_level_count,
/// glwe_modular_std_dev,
/// ciphertext_modulus,
/// &mut encryption_generator,
/// );
///
/// let mut output_glwe_ciphertext =
/// GlweCiphertext::new(0u64, glwe_size, polynomial_size, ciphertext_modulus);
///
/// glwe_relinearisation(&tensor_output, &glwe_relin_key, &mut output_glwe_ciphertext);
///
/// // Decrypt the output glwe ciphertext
/// let mut output_plaintext = PlaintextList::new(0u64, PlaintextCount(polynomial_size.0));
///
/// decrypt_glwe_ciphertext(
/// &glwe_secret_key,
/// &output_glwe_ciphertext,
/// &mut output_plaintext,
/// );
/// output_plaintext
/// .iter_mut()
/// .for_each(|elt| *elt.0 = decomposer.closest_representable(*elt.0));
///
/// // Get the raw vector
/// let mut cleartext = output_plaintext.into_container();
/// // Remove the encoding
/// cleartext
/// .iter_mut()
/// .for_each(|elt| *elt = *elt >> output_log_delta);
/// // Get the list immutably
/// let cleartext = cleartext;
///
/// // Check we recovered the correct message
/// cleartext
/// .iter()
/// .zip(scaled_product.iter())
/// .for_each(|(&elt, coeff)| assert_eq!(elt, *coeff));
/// ```
/// based on algorithm 1 of `<https://eprint.iacr.org/2021/729.pdf>` in the eprint paper the result
/// of the division is rounded,
/// here the division in u128 performs a floor hence the induced error might be twice as large
pub fn glwe_tensor_product<InputCont, Scalar>(
input_glwe_ciphertext_lhs: &GlweCiphertext<InputCont>,
input_glwe_ciphertext_rhs: &GlweCiphertext<InputCont>,
scale: Scalar,
) -> GlweCiphertext<Vec<Scalar>>
where
Scalar: UnsignedTorus + CastInto<u128> + CastFrom<u128>,
InputCont: Container<Element = Scalar>,
{
assert!(
input_glwe_ciphertext_lhs.polynomial_size().0
== input_glwe_ciphertext_rhs.polynomial_size().0,
"The input glwe ciphertexts do not have the same polynomial size. The polynomial size of \
the lhs is {}, while for the rhs it is {}.",
input_glwe_ciphertext_lhs.polynomial_size().0,
input_glwe_ciphertext_rhs.polynomial_size().0
);
assert!(
input_glwe_ciphertext_lhs.glwe_size().0 == input_glwe_ciphertext_rhs.glwe_size().0,
"The input glwe ciphertexts do not have the same glwe size. The glwe size of the lhs is \
{}, while for the rhs it is {}.",
input_glwe_ciphertext_lhs.glwe_size().0,
input_glwe_ciphertext_rhs.glwe_size().0
);
assert_eq!(
input_glwe_ciphertext_lhs.ciphertext_modulus(),
input_glwe_ciphertext_rhs.ciphertext_modulus()
);
let k = input_glwe_ciphertext_lhs.glwe_size().to_glwe_dimension().0;
// This is k + k*(k-1)/2 + k: k square terms, k*(k-1)/2 cross terms, k linear terms
let new_k = GlweDimension(k * (k + 3) / 2);
let mut output_glwe_ciphertext = GlweCiphertextOwned::new(
Scalar::ZERO,
new_k.to_glwe_size(),
input_glwe_ciphertext_lhs.polynomial_size(),
input_glwe_ciphertext_lhs.ciphertext_modulus(),
);
let mut output_mask = output_glwe_ciphertext.get_mut_mask();
let mut output_mask_poly_list = output_mask.as_mut_polynomial_list();
let mut iter_output_mask = output_mask_poly_list.iter_mut();
let input_lhs = PolynomialList::from_container(
input_glwe_ciphertext_lhs
.get_mask()
.as_ref()
.iter()
.map(|&x| <Scalar as CastInto<u128>>::cast_into(x))
.collect::<Vec<_>>(),
input_glwe_ciphertext_lhs.polynomial_size(),
);
let input_rhs = PolynomialList::from_container(
input_glwe_ciphertext_rhs
.get_mask()
.as_ref()
.iter()
.map(|&x| <Scalar as CastInto<u128>>::cast_into(x))
.collect::<Vec<_>>(),
input_glwe_ciphertext_rhs.polynomial_size(),
);
for (i, a_lhs_i) in input_lhs.iter().enumerate() {
for (j, a_rhs_j) in input_rhs.iter().enumerate() {
if i == j {
//tensor elements corresponding to key -s_i^2
let mut temp_poly_sq = Polynomial::new(0u128, a_lhs_i.polynomial_size());
polynomial_wrapping_add_mul_assign(&mut temp_poly_sq, &a_lhs_i, &a_rhs_j);
let mut output_poly_sq = iter_output_mask.next().unwrap();
output_poly_sq
.as_mut()
.iter_mut()
.zip(temp_poly_sq.as_ref().iter())
.for_each(|(dest, &source)| {
*dest =
Scalar::cast_from(source / <Scalar as CastInto<u128>>::cast_into(scale))
});
//tensor elements corresponding to key s_i
let mut temp_poly_s1 = Polynomial::new(0u128, a_lhs_i.polynomial_size());
polynomial_wrapping_add_mul_assign(
&mut temp_poly_s1,
&a_lhs_i,
&Polynomial::from_container(
input_glwe_ciphertext_rhs
.get_body()
.as_ref()
.iter()
.map(|&x| <Scalar as CastInto<u128>>::cast_into(x))
.collect::<Vec<_>>(),
),
);
let mut temp_poly_s2 = Polynomial::new(0u128, a_lhs_i.polynomial_size());
polynomial_wrapping_add_mul_assign(
&mut temp_poly_s2,
&Polynomial::from_container(
input_glwe_ciphertext_lhs
.get_body()
.as_ref()
.iter()
.map(|&x| <Scalar as CastInto<u128>>::cast_into(x))
.collect::<Vec<_>>(),
),
&a_rhs_j,
);
polynomial_wrapping_add_assign(&mut temp_poly_s1, &temp_poly_s2);
let mut output_poly_s = iter_output_mask.next().unwrap();
output_poly_s
.as_mut()
.iter_mut()
.zip(temp_poly_s1.as_ref().iter())
.for_each(|(dest, &source)| {
*dest =
Scalar::cast_from(source / <Scalar as CastInto<u128>>::cast_into(scale))
});
} else {
//when i and j are different we only compute the terms where j < i
if j < i {
//tensor element corresponding to key -s_i*s_j
let mut temp_poly = Polynomial::new(0u128, a_lhs_i.polynomial_size());
polynomial_wrapping_add_mul_assign(&mut temp_poly, &a_lhs_i, &a_rhs_j);
polynomial_wrapping_add_mul_assign(
&mut temp_poly,
&input_lhs.get(j),
&input_rhs.get(i),
);
let mut output_poly = iter_output_mask.next().unwrap();
output_poly
.as_mut()
.iter_mut()
.zip(temp_poly.as_ref().iter())
.for_each(|(dest, &source)| {
*dest = Scalar::cast_from(
source / <Scalar as CastInto<u128>>::cast_into(scale),
)
});
}
}
}
}
//tensor element corresponding to the body
let mut temp_poly_body = Polynomial::new(0u128, input_glwe_ciphertext_lhs.polynomial_size());
polynomial_wrapping_add_mul_assign(
&mut temp_poly_body,
&Polynomial::from_container(
input_glwe_ciphertext_lhs
.get_body()
.as_ref()
.iter()
.map(|&x| <Scalar as CastInto<u128>>::cast_into(x))
.collect::<Vec<_>>(),
),
&Polynomial::from_container(
input_glwe_ciphertext_rhs
.get_body()
.as_ref()
.iter()
.map(|&x| <Scalar as CastInto<u128>>::cast_into(x))
.collect::<Vec<_>>(),
),
);
let mut output_body = output_glwe_ciphertext.get_mut_body();
let mut output_poly_body = output_body.as_mut_polynomial();
output_poly_body
.as_mut()
.iter_mut()
.zip(temp_poly_body.as_ref().iter())
.for_each(|(dest, &source)| {
*dest = Scalar::cast_from(source / <Scalar as CastInto<u128>>::cast_into(scale))
});
output_glwe_ciphertext
}
/// Relinearise the [`GLWE ciphertext`](`GlweCiphertext`) that is output by the
/// glwe_tensor_product operation using a [`GLWE relinearisation key`](`GlweRelinearisationKey`).
pub fn glwe_relinearisation<InputCont, KeyCont, OutputCont, Scalar>(
input_glwe_ciphertext: &GlweCiphertext<InputCont>,
relinearisation_key: &GlweRelinearisationKey<KeyCont>,
output_glwe_ciphertext: &mut GlweCiphertext<OutputCont>,
) where
Scalar: UnsignedTorus,
InputCont: Container<Element = Scalar>,
KeyCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
{
assert_eq!(
relinearisation_key.glwe_dimension().0 * (relinearisation_key.glwe_dimension().0 + 3) / 2,
input_glwe_ciphertext.glwe_size().to_glwe_dimension().0
);
assert_eq!(
relinearisation_key.glwe_size(),
output_glwe_ciphertext.glwe_size()
);
assert_eq!(
relinearisation_key.polynomial_size(),
input_glwe_ciphertext.polynomial_size()
);
assert_eq!(
relinearisation_key.polynomial_size(),
output_glwe_ciphertext.polynomial_size()
);
// Clear the output ciphertext, as it will get updated gradually
output_glwe_ciphertext.as_mut().fill(Scalar::ZERO);
// Copy the input body to the output ciphertext
polynomial_wrapping_add_assign(
&mut output_glwe_ciphertext.get_mut_body().as_mut_polynomial(),
&input_glwe_ciphertext.get_body().as_polynomial(),
);
// We instantiate a decomposer
let decomposer = SignedDecomposer::new(
relinearisation_key.decomposition_base_log(),
relinearisation_key.decomposition_level_count(),
);
let mut relin_key_iter = relinearisation_key.iter();
let input_glwe_mask = input_glwe_ciphertext.get_mask();
let input_glwe_mask_poly_list = input_glwe_mask.as_polynomial_list();
let mut input_poly_iter = input_glwe_mask_poly_list.iter();
for i in 0..relinearisation_key.glwe_size().0 - 1 {
for _ in 0..i + 1 {
let ksk = relin_key_iter.next().unwrap();
let pol = input_poly_iter.next().unwrap();
let mut decomposition_iter = decomposer.decompose_slice(pol.as_ref());
// loop over the number of levels in reverse (from highest to lowest)
for level_key_ciphertext in ksk.iter().rev() {
let decomposed = decomposition_iter.next_term().unwrap();
polynomial_list_wrapping_sub_scalar_mul_assign(
&mut output_glwe_ciphertext.as_mut_polynomial_list(),
&level_key_ciphertext.as_polynomial_list(),
&Polynomial::from_container(decomposed.as_slice()),
);
}
}
let pol = input_poly_iter.next().unwrap();
polynomial_wrapping_add_assign(
&mut output_glwe_ciphertext.as_mut_polynomial_list().get_mut(i),
&pol,
)
}
}
pub fn tensor_mult_with_relin<InputCont, KeyCont, OutputCont, Scalar>(
input_glwe_ciphertext_lhs: &GlweCiphertext<InputCont>,
input_glwe_ciphertext_rhs: &GlweCiphertext<InputCont>,
scale: Scalar,
relinearisation_key: &GlweRelinearisationKey<KeyCont>,
output_glwe_ciphertext: &mut GlweCiphertext<OutputCont>,
) where
Scalar: UnsignedTorus + CastInto<u128> + CastFrom<u128>,
InputCont: Container<Element = Scalar>,
KeyCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
{
let tensor_output = glwe_tensor_product(
&input_glwe_ciphertext_lhs,
&input_glwe_ciphertext_rhs,
scale,
);
glwe_relinearisation(&tensor_output, &relinearisation_key, output_glwe_ciphertext);
}
pub fn packed_mult<InputCont, KeyCont, OutputCont, Scalar>(
input_lwe_ciphertext_list_1: &LweCiphertextList<InputCont>,
input_lwe_ciphertext_list_2: &LweCiphertextList<InputCont>,
lwe_pubfpksk: &LwePublicFunctionalPackingKeyswitchKey<KeyCont>,
relinearisation_key: &GlweRelinearisationKey<KeyCont>,
scale: Scalar,
output_lwe_ciphertext_list: &mut LweCiphertextList<OutputCont>,
) where
Scalar: UnsignedTorus + CastInto<u128> + CastFrom<u128>,
KeyCont: Container<Element = Scalar>,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
{
let mut packed_glwe_1 =
GlweCiphertext::new(Scalar::ZERO,
lwe_pubfpksk.output_glwe_size(),
lwe_pubfpksk.output_polynomial_size(),
lwe_pubfpksk.ciphertext_modulus());
public_functional_keyswitch_lwe_ciphertexts_into_glwe_ciphertext(
&lwe_pubfpksk,
&mut packed_glwe_1,
&input_lwe_ciphertext_list_1,
| x: Vec<Scalar>| {
let mut packed1: Vec<Scalar> = vec![Scalar::ZERO;lwe_pubfpksk.output_polynomial_size().0];
x.iter().enumerate().for_each(|(iter,y)| packed1[iter] = *y);
Polynomial::from_container(packed1)
}
);
let mut packed_glwe_2 =
GlweCiphertext::new(Scalar::ZERO,
lwe_pubfpksk.output_glwe_size(),
lwe_pubfpksk.output_polynomial_size(),
lwe_pubfpksk.ciphertext_modulus());
public_functional_keyswitch_lwe_ciphertexts_into_glwe_ciphertext(
&lwe_pubfpksk,
&mut packed_glwe_2,
&input_lwe_ciphertext_list_2,
| x| {
let mut packed2: Vec<Scalar> = vec![Scalar::ZERO;lwe_pubfpksk.output_polynomial_size().0];
x.iter().enumerate().for_each(|(iter,y)| packed2[input_lwe_ciphertext_list_1.lwe_ciphertext_count().0*iter] = *y);
Polynomial::from_container(packed2)
},
);
let mut relin_glwe_ciphertext =
GlweCiphertext::new(Scalar::ZERO, lwe_pubfpksk.output_glwe_size(),
lwe_pubfpksk.output_polynomial_size(),
lwe_pubfpksk.ciphertext_modulus());
tensor_mult_with_relin(
&packed_glwe_1,
&packed_glwe_2,
scale,
&relinearisation_key,
&mut relin_glwe_ciphertext,
);
output_lwe_ciphertext_list.iter_mut().enumerate().
for_each(|(iter, mut el)| extract_lwe_sample_from_glwe_ciphertext(&relin_glwe_ciphertext,
&mut el,
MonomialDegree(iter*(input_lwe_ciphertext_list_1.lwe_ciphertext_count().0+1))));
}
pub fn packed_sum_product<InputCont, KeyCont, OutputCont, Scalar>(
input_lwe_ciphertext_list_1: &LweCiphertextList<InputCont>,
input_lwe_ciphertext_list_2: &LweCiphertextList<InputCont>,
lwe_pubfpksk: &LwePublicFunctionalPackingKeyswitchKey<KeyCont>,
relinearisation_key: &GlweRelinearisationKey<KeyCont>,
scale: Scalar,
output_lwe_ciphertext: &mut LweCiphertext<OutputCont>,
) where
Scalar: UnsignedTorus + CastInto<u128> + CastFrom<u128>,
KeyCont: Container<Element = Scalar>,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
{
let mut packed_glwe_1 =
GlweCiphertext::new(Scalar::ZERO,
lwe_pubfpksk.output_glwe_size(),
lwe_pubfpksk.output_polynomial_size(),
lwe_pubfpksk.ciphertext_modulus());
public_functional_keyswitch_lwe_ciphertexts_into_glwe_ciphertext(
&lwe_pubfpksk,
&mut packed_glwe_1,
&input_lwe_ciphertext_list_1,
| x: Vec<Scalar>| {
let mut packed1: Vec<Scalar> = vec![Scalar::ZERO;lwe_pubfpksk.output_polynomial_size().0];
x.iter().enumerate().for_each(|(iter,y)| packed1[iter] = *y);
Polynomial::from_container(packed1)
}
);
let mut packed_glwe_2 =
GlweCiphertext::new(Scalar::ZERO,
lwe_pubfpksk.output_glwe_size(),
lwe_pubfpksk.output_polynomial_size(),
lwe_pubfpksk.ciphertext_modulus());
public_functional_keyswitch_lwe_ciphertexts_into_glwe_ciphertext(
&lwe_pubfpksk,
&mut packed_glwe_2,
&input_lwe_ciphertext_list_2,
| x| {
let mut packed2: Vec<Scalar> = vec![Scalar::ZERO;lwe_pubfpksk.output_polynomial_size().0];
x.iter().enumerate().for_each(|(iter,y)|
packed2[input_lwe_ciphertext_list_1.lwe_ciphertext_count().0-1-iter] = *y);
Polynomial::from_container(packed2)
},
);
let mut relin_glwe_ciphertext =
GlweCiphertext::new(Scalar::ZERO, lwe_pubfpksk.output_glwe_size(),
lwe_pubfpksk.output_polynomial_size(),
lwe_pubfpksk.ciphertext_modulus());
tensor_mult_with_relin(
&packed_glwe_1,
&packed_glwe_2,
scale,
&relinearisation_key,
&mut relin_glwe_ciphertext,
);
extract_lwe_sample_from_glwe_ciphertext(&relin_glwe_ciphertext,
output_lwe_ciphertext,
MonomialDegree(input_lwe_ciphertext_list_1.lwe_ciphertext_count().0-1));
}

View File

@@ -0,0 +1,202 @@
//! Module containing primitives pertaining to LWE ciphertext private functional keyswitch and
//! packing keyswitch.
//!
//! Formal description can be found in: \
//! &nbsp;&nbsp;&nbsp;&nbsp; Chillotti, I., Gama, N., Georgieva, M. et al. \
//! &nbsp;&nbsp;&nbsp;&nbsp; TFHE: Fast Fully Homomorphic Encryption Over the Torus. \
//! &nbsp;&nbsp;&nbsp;&nbsp; J. Cryptol 33, 3491 (2020). \
//! &nbsp;&nbsp;&nbsp;&nbsp; <https://doi.org/10.1007/s00145-019-09319-x>
use crate::core_crypto::algorithms::polynomial_algorithms::*;
//use crate::core_crypto::algorithms::slice_algorithms::*;
use crate::core_crypto::commons::math::decomposition::SignedDecomposer;
//use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
/// Apply a public functional packing keyswitch on an input
/// [`LWE ciphertext list`](`LweCiphertextList`) and write
/// the result in an output [`GLWE ciphertext`](`GlweCiphertext`).
/// # Example
/// ```
/// //define the inputs for the public functional key switching
/// use tfhe::core_crypto::algorithms::polynomial_algorithms::*;
/// use tfhe::core_crypto::prelude::*;
///
/// let lwe_dimension = LweDimension(742);
///
/// // Create the PRNG
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
///
/// let lwe_secret_key: LweSecretKeyOwned<u64> =
/// LweSecretKey::generate_new_binary(lwe_dimension, &mut secret_generator);
/// let glwe_size = GlweSize(2);
/// let polynomial_size = PolynomialSize(1024);
/// let glwe_secret_key: GlweSecretKeyOwned<u64> = GlweSecretKey::generate_new_binary(
/// glwe_size.to_glwe_dimension(),
/// polynomial_size,
/// &mut secret_generator,
/// );
/// let decomp_base_log = DecompositionBaseLog(8);
/// let decomp_level_count = DecompositionLevelCount(3);
/// let ciphertext_modulus = CiphertextModulus::new_native();
/// let mut lwe_pubfpksk = LwePublicFunctionalPackingKeyswitchKey::new(
/// 0u64,
/// decomp_base_log,
/// decomp_level_count,
/// lwe_dimension,
/// glwe_size,
/// polynomial_size,
/// ciphertext_modulus,
/// );
/// let glwe_modular_std_dev = StandardDev(0.00000000000000029403601535432533);
/// let mut encryption_generator =
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
/// generate_lwe_public_functional_packing_keyswitch_key(
/// &lwe_secret_key,
/// &glwe_secret_key,
/// &mut lwe_pubfpksk,
/// glwe_modular_std_dev,
/// &mut encryption_generator,
/// );
///
/// let lwe_ciphertext_count = LweCiphertextCount(20);
/// let mut lwe_list = LweCiphertextList::new(
/// 0u64,
/// lwe_dimension.to_lwe_size(),
/// lwe_ciphertext_count,
/// ciphertext_modulus,
/// );
/// let lwe_plaintext_list = PlaintextList::new(1u64 << 59, PlaintextCount(20));
/// encrypt_lwe_ciphertext_list(
/// &lwe_secret_key,
/// &mut lwe_list,
/// &lwe_plaintext_list,
/// glwe_modular_std_dev,
/// &mut encryption_generator,
/// );
///
/// let mut output_glwe_ciphertext =
/// GlweCiphertext::new(0u64, glwe_size, polynomial_size, ciphertext_modulus);
/// public_functional_keyswitch_lwe_ciphertexts_into_glwe_ciphertext(
/// &mut lwe_pubfpksk,
/// &mut output_glwe_ciphertext,
/// &lwe_list,
/// |mut x| {
/// let mut sum = 0u64;
/// x.iter().for_each(|y| sum = sum.wrapping_add(*y));
/// let mut temp = vec![sum];
/// temp.resize(polynomial_size.0, 0u64);
/// Polynomial::from_container(temp)
/// },
/// );
///
/// let mut output_plaintext_list = PlaintextList::new(0u64, PlaintextCount(polynomial_size.0));
/// let decomposer = SignedDecomposer::new(DecompositionBaseLog(1), DecompositionLevelCount(4));
///
/// decrypt_glwe_ciphertext(
/// &glwe_secret_key,
/// &output_glwe_ciphertext,
/// &mut output_plaintext_list,
/// );
/// output_plaintext_list
/// .iter_mut()
/// .for_each(|x| *x.0 = decomposer.closest_representable(*x.0));
///
/// // Get the raw vecor
/// let mut cleartext = output_plaintext_list.into_container();
/// // Remove the encoding
/// cleartext.iter_mut().for_each(|x| *x = *x >> 59);
/// // Get the list immutably
/// let cleartext = cleartext;
///
/// // Check we get the correct result
/// for (index, clear) in cleartext.iter().enumerate() {
/// if index == 0 {
/// assert_eq!(20, *clear);
/// } else {
/// assert_eq!(0, *clear);
/// }
/// }
/// ```
pub fn public_functional_keyswitch_lwe_ciphertexts_into_glwe_ciphertext<
KeyCont,
InputCont,
OutputCont,
Func,
Scalar,
>(
lwe_pubfpksk: &LwePublicFunctionalPackingKeyswitchKey<KeyCont>,
output_glwe_ciphertext: &mut GlweCiphertext<OutputCont>,
input_lwe_ciphertext_list: &LweCiphertextList<InputCont>,
f: Func,
) where
Scalar: UnsignedTorus,
KeyCont: Container<Element = Scalar>,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
Func: Fn(Vec<Scalar>) -> Polynomial<Vec<Scalar>>,
{
assert_eq!(
output_glwe_ciphertext.polynomial_size(),
lwe_pubfpksk.output_polynomial_size()
);
assert_eq!(
output_glwe_ciphertext.glwe_size(),
lwe_pubfpksk.output_glwe_size()
);
assert_eq!(
input_lwe_ciphertext_list.lwe_size().to_lwe_dimension(),
lwe_pubfpksk.input_lwe_key_dimension()
);
//evaluate the function on this list of first elements
let mut list_output_function =
Vec::with_capacity(input_lwe_ciphertext_list.lwe_ciphertext_count().0);
for i in 0..input_lwe_ciphertext_list.lwe_size().to_lwe_dimension().0 {
//get list of ith elements of the input lwes
let vec_of_ai: Vec<Scalar> = input_lwe_ciphertext_list
.iter()
.map(|lwe| lwe.get_mask().as_ref()[i])
.collect();
list_output_function.push(f(vec_of_ai));
}
// We reset the output
output_glwe_ciphertext.as_mut().fill(Scalar::ZERO);
let vec_of_b: Vec<Scalar> = input_lwe_ciphertext_list
.iter()
.map(|lwe| *lwe.get_body().data)
.collect();
assert!(f(vec_of_b.clone()).polynomial_size() == output_glwe_ciphertext.polynomial_size(),
"the polynomial size of the output_glwe_ciphertext value needs to be equal to the polynomial size of the output of the function f");
//initiate the body of the output glwe ciphertext
output_glwe_ciphertext
.get_mut_body()
.as_mut()
.copy_from_slice(f(vec_of_b).as_ref());
//decompose the result of the function
// We instantiate a decomposer
let decomposer = SignedDecomposer::new(
lwe_pubfpksk.decomposition_base_log(),
lwe_pubfpksk.decomposition_level_count(),
);
for (keyswitch_key_block, output_function) in
lwe_pubfpksk.iter().zip(list_output_function.iter_mut())
{
let mut decomposition_iter = decomposer.decompose_slice(output_function.as_ref());
// loop over the number of levels in reverse (from highest to lowest)
for level_key_ciphertext in keyswitch_key_block.iter().rev() {
let decomposed = decomposition_iter.next_term().unwrap();
polynomial_list_wrapping_sub_scalar_mul_assign(
&mut output_glwe_ciphertext.as_mut_polynomial_list(),
&level_key_ciphertext.as_polynomial_list(),
&Polynomial::from_container(decomposed.as_slice()),
);
}
}
}

View File

@@ -0,0 +1,359 @@
//! Module containing primitives pertaining to [`LWE public functional packing keyswitch key
//! generation`](`LwePublicFunctionalPackingKeyswitchKey`).
use crate::core_crypto::algorithms::*;
use crate::core_crypto::commons::dispersion::DispersionParameter;
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
use crate::core_crypto::commons::math::decomposition::{DecompositionLevel, DecompositionTerm};
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
use rayon::prelude::*;
/// Fill an [`LWE public functional packing keyswitch
/// key`](`LwePublicFunctionalPackingKeyswitchKey`) with an actual key.
///
/// # Example
/// ```
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// use tfhe::core_crypto::prelude::*;
/// let lwe_dimension = LweDimension(8);
/// let ciphertext_modulus = CiphertextModulus::new_native();
///
/// // Create the PRNG
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
///
/// let input_lwe_secret_key =
/// LweSecretKey::generate_new_binary(lwe_dimension, &mut secret_generator);
/// let glwe_size = GlweSize(2);
/// let polynomial_size = PolynomialSize(1024);
/// let output_glwe_secret_key = GlweSecretKey::generate_new_binary(
/// glwe_size.to_glwe_dimension(),
/// polynomial_size,
/// &mut secret_generator,
/// );
///
/// let decomp_base_log = DecompositionBaseLog(8);
/// let decomp_level_count = DecompositionLevelCount(3);
/// let mut lwe_pubfpksk = LwePublicFunctionalPackingKeyswitchKey::new(
/// 0u64,
/// decomp_base_log,
/// decomp_level_count,
/// lwe_dimension,
/// glwe_size,
/// polynomial_size,
/// ciphertext_modulus,
/// );
/// let glwe_modular_std_dev = StandardDev(0.00000000000000029403601535432533);
/// let mut encryption_generator =
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
/// generate_lwe_public_functional_packing_keyswitch_key(
/// &input_lwe_secret_key,
/// &output_glwe_secret_key,
/// &mut lwe_pubfpksk,
/// glwe_modular_std_dev,
/// &mut encryption_generator,
/// );
/// ```
pub fn generate_lwe_public_functional_packing_keyswitch_key<
Scalar,
InputKeyCont,
OutputKeyCont,
KSKeyCont,
Gen,
>(
input_lwe_secret_key: &LweSecretKey<InputKeyCont>,
output_glwe_secret_key: &GlweSecretKey<OutputKeyCont>,
lwe_pubfpksk: &mut LwePublicFunctionalPackingKeyswitchKey<KSKeyCont>,
noise_parameters: impl DispersionParameter + Sync,
generator: &mut EncryptionRandomGenerator<Gen>,
) where
Scalar: UnsignedTorus,
InputKeyCont: Container<Element = Scalar>,
OutputKeyCont: Container<Element = Scalar>,
KSKeyCont: ContainerMut<Element = Scalar>,
Gen: ByteRandomGenerator,
{
assert!(
input_lwe_secret_key.lwe_dimension() == lwe_pubfpksk.input_lwe_key_dimension(),
"Mismatched LweDimension between input_lwe_secret_key {:?} and lwe_pfpksk input dimension \
{:?}.",
input_lwe_secret_key.lwe_dimension(),
lwe_pubfpksk.input_lwe_key_dimension()
);
assert!(
output_glwe_secret_key.glwe_dimension() == lwe_pubfpksk.output_glwe_key_dimension(),
"Mismatched GlweDimension between output_glwe_secret_key {:?} and lwe_pfpksk output \
dimension {:?}.",
output_glwe_secret_key.glwe_dimension(),
lwe_pubfpksk.output_glwe_key_dimension()
);
assert!(
output_glwe_secret_key.polynomial_size() == lwe_pubfpksk.output_polynomial_size(),
"Mismatched PolynomialSize between output_glwe_secret_key {:?} and lwe_pfpksk output \
polynomial size {:?}.",
output_glwe_secret_key.polynomial_size(),
lwe_pubfpksk.output_polynomial_size()
);
// We instantiate a buffer
let mut messages = PlaintextListOwned::new(
Scalar::ZERO,
PlaintextCount(
lwe_pubfpksk.decomposition_level_count().0 * lwe_pubfpksk.output_polynomial_size().0,
),
);
// We retrieve decomposition arguments
let decomp_level_count = lwe_pubfpksk.decomposition_level_count();
let decomp_base_log = lwe_pubfpksk.decomposition_base_log();
let polynomial_size = lwe_pubfpksk.output_polynomial_size();
let last_key_iter_bit = [Scalar::MAX];
// add minus one for the function which will be applied to the decomposed body
// ( Scalar::MAX = -Scalar::ONE )
let input_key_bit_iter = input_lwe_secret_key
.as_ref()
.iter()
.chain(last_key_iter_bit.iter());
//TODO should we rename fork_pfpksk_to_pfpksk_chunks?
let gen_iter = generator
.fork_pfpksk_to_pfpksk_chunks::<Scalar>(
decomp_level_count,
output_glwe_secret_key.glwe_dimension().to_glwe_size(),
output_glwe_secret_key.polynomial_size(),
input_lwe_secret_key.lwe_dimension().to_lwe_size(),
)
.unwrap();
// loop over the before key blocks
for ((&input_key_bit, mut keyswitch_key_block), mut loop_generator) in input_key_bit_iter
.zip(lwe_pubfpksk.iter_mut())
.zip(gen_iter)
{
//we reset the buffer
// We fill the buffer with the powers of the decomposition scale times the key bits
for (level, mut message) in (1..=decomp_level_count.0)
.map(DecompositionLevel)
.zip(messages.chunks_exact_mut(polynomial_size.0))
{
message.as_mut()[0] = DecompositionTerm::new(level, decomp_base_log, input_key_bit)
.to_recomposition_summand();
}
// We encrypt the buffer
encrypt_glwe_ciphertext_list(
output_glwe_secret_key,
&mut keyswitch_key_block,
&messages,
noise_parameters,
&mut loop_generator,
)
}
}
/// Parallel variant of [`generate_lwe_public_functional_packing_keyswitch_key`]. You may want to
/// use this variant for better key generation times.
pub fn par_generate_lwe_public_functional_packing_keyswitch_key<
Scalar,
InputKeyCont,
OutputKeyCont,
KSKeyCont,
Gen,
>(
input_lwe_secret_key: &LweSecretKey<InputKeyCont>,
output_glwe_secret_key: &GlweSecretKey<OutputKeyCont>,
lwe_pubfpksk: &mut LwePublicFunctionalPackingKeyswitchKey<KSKeyCont>,
noise_parameters: impl DispersionParameter + Sync,
generator: &mut EncryptionRandomGenerator<Gen>,
) where
Scalar: UnsignedTorus + Sync + Send,
InputKeyCont: Container<Element = Scalar>,
OutputKeyCont: Container<Element = Scalar> + Sync,
KSKeyCont: ContainerMut<Element = Scalar> + Sync,
Gen: ParallelByteRandomGenerator,
{
assert!(
input_lwe_secret_key.lwe_dimension() == lwe_pubfpksk.input_lwe_key_dimension(),
"Mismatched LweDimension between input_lwe_secret_key {:?} and lwe_pfpksk input dimension \
{:?}.",
input_lwe_secret_key.lwe_dimension(),
lwe_pubfpksk.input_lwe_key_dimension()
);
assert!(
output_glwe_secret_key.glwe_dimension() == lwe_pubfpksk.output_glwe_key_dimension(),
"Mismatched GlweDimension between output_glwe_secret_key {:?} and lwe_pfpksk output \
dimension {:?}.",
output_glwe_secret_key.glwe_dimension(),
lwe_pubfpksk.output_glwe_key_dimension()
);
assert!(
output_glwe_secret_key.polynomial_size() == lwe_pubfpksk.output_polynomial_size(),
"Mismatched PolynomialSize between output_glwe_secret_key {:?} and lwe_pfpksk output \
polynomial size {:?}.",
output_glwe_secret_key.polynomial_size(),
lwe_pubfpksk.output_polynomial_size()
);
// We retrieve decomposition arguments
let decomp_level_count = lwe_pubfpksk.decomposition_level_count();
let decomp_base_log = lwe_pubfpksk.decomposition_base_log();
let polynomial_size = lwe_pubfpksk.output_polynomial_size();
let last_key_iter_bit = [Scalar::MAX];
// add minus one for the function which will be applied to the decomposed body
// ( Scalar::MAX = -Scalar::ONE )
let input_key_bit_iter = input_lwe_secret_key
.as_ref()
.par_iter()
.chain(last_key_iter_bit.par_iter());
let gen_iter = generator
.par_fork_pfpksk_to_pfpksk_chunks::<Scalar>(
decomp_level_count,
output_glwe_secret_key.glwe_dimension().to_glwe_size(),
output_glwe_secret_key.polynomial_size(),
input_lwe_secret_key.lwe_dimension().to_lwe_size(),
)
.unwrap();
let plaintext_count = PlaintextCount(
lwe_pubfpksk.decomposition_level_count().0 * lwe_pubfpksk.output_polynomial_size().0,
);
// loop over the before key blocks
input_key_bit_iter
.zip(lwe_pubfpksk.par_iter_mut())
.zip(gen_iter)
.for_each(
|((&input_key_bit, mut keyswitch_key_block), mut loop_generator)| {
// We instantiate a buffer
let mut messages = PlaintextListOwned::new(Scalar::ZERO, plaintext_count);
// We fill the buffer with the powers of the key bits
for (level, mut message) in (1..=decomp_level_count.0)
.map(DecompositionLevel)
.zip(messages.chunks_exact_mut(polynomial_size.0))
{
message.as_mut()[0] =
DecompositionTerm::new(level, decomp_base_log, input_key_bit)
.to_recomposition_summand();
}
// We encrypt the buffer
encrypt_glwe_ciphertext_list(
output_glwe_secret_key,
&mut keyswitch_key_block,
&messages,
noise_parameters,
&mut loop_generator,
)
},
);
}
#[cfg(test)]
mod test {
use crate::core_crypto::commons::generators::DeterministicSeeder;
use crate::core_crypto::commons::math::random::Seed;
use crate::core_crypto::prelude::*;
#[test]
fn test_pubfpksk_list_gen_equivalence() {
const NB_TESTS: usize = 10;
for _ in 0..NB_TESTS {
// DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield
// correct computations
let glwe_dimension =
GlweDimension(crate::core_crypto::commons::test_tools::random_usize_between(5..10));
let polynomial_size = PolynomialSize(
crate::core_crypto::commons::test_tools::random_usize_between(5..10),
);
let pubfpksk_level_count = DecompositionLevelCount(
crate::core_crypto::commons::test_tools::random_usize_between(2..5),
);
let pubfpksk_base_log = DecompositionBaseLog(
crate::core_crypto::commons::test_tools::random_usize_between(2..5),
);
let ciphertext_modulus = CiphertextModulus::new_native();
let common_encryption_seed =
Seed(crate::core_crypto::commons::test_tools::random_uint_between(0..u128::MAX));
let var_small = Variance::from_variance(2f64.powf(-80.0));
// Create the PRNG
let mut seeder = new_seeder();
let mut secret_generator =
SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
let glwe_sk: GlweSecretKeyOwned<u64> = allocate_and_generate_new_binary_glwe_secret_key(
glwe_dimension,
polynomial_size,
&mut secret_generator,
);
let lwe_big_sk = glwe_sk.clone().into_lwe_secret_key();
let mut par_pubfpksk = LwePublicFunctionalPackingKeyswitchKey::new(
0u64,
pubfpksk_base_log,
pubfpksk_level_count,
lwe_big_sk.lwe_dimension(),
glwe_dimension.to_glwe_size(),
polynomial_size,
ciphertext_modulus,
);
let mut ser_pubfpksk = LwePublicFunctionalPackingKeyswitchKey::new(
0u64,
pubfpksk_base_log,
pubfpksk_level_count,
lwe_big_sk.lwe_dimension(),
glwe_dimension.to_glwe_size(),
polynomial_size,
ciphertext_modulus,
);
let mut seeder =
DeterministicSeeder::<ActivatedRandomGenerator>::new(common_encryption_seed);
let mut encryption_generator =
EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(
seeder.seed(),
&mut seeder,
);
par_generate_lwe_public_functional_packing_keyswitch_key(
&lwe_big_sk,
&glwe_sk,
&mut par_pubfpksk,
var_small,
&mut encryption_generator,
);
let mut seeder =
DeterministicSeeder::<ActivatedRandomGenerator>::new(common_encryption_seed);
let mut encryption_generator =
EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(
seeder.seed(),
&mut seeder,
);
generate_lwe_public_functional_packing_keyswitch_key(
&lwe_big_sk,
&glwe_sk,
&mut ser_pubfpksk,
var_small,
&mut encryption_generator,
);
assert_eq!(par_pubfpksk, ser_pubfpksk)
}
}
}

View File

@@ -0,0 +1,330 @@
//! Module containing primitives pertaining to LWE trace pacling keyswitch.
use crate::core_crypto::algorithms::glwe_keyswitch::*;
use crate::core_crypto::algorithms::polynomial_algorithms::*;
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
/// Apply a trace packing keyswitch on an input [`LWE ciphertext list`](`LweCiphertextList`) and
/// pack the result in an output [`GLWE ciphertext`](`GlweCiphertext`).
///
/// ```
/// use tfhe::core_crypto::prelude::*;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for LweTracePackingKeyswitchKey creation
/// let lwe_dimension = LweDimension(60);
/// let lwe_count = LweCiphertextCount(25);
/// let polynomial_size = PolynomialSize(32);
/// let glwe_dimension = GlweDimension(2);
/// let lwe_modular_std_dev = StandardDev(0.00000000000000000000001);
/// let ciphertext_modulus = CiphertextModulus::new_native();
///
/// let mut seeder = new_seeder();
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
/// let lwe_secret_key =
/// allocate_and_generate_new_binary_lwe_secret_key(lwe_dimension, &mut secret_generator);
///
/// let mut glwe_secret_key = GlweSecretKey::new_empty_key(0u64, glwe_dimension, polynomial_size);
///
/// generate_tpksk_output_glwe_secret_key(&lwe_secret_key, &mut glwe_secret_key);
///
/// let decomp_base_log = DecompositionBaseLog(2);
/// let decomp_level_count = DecompositionLevelCount(8);
/// let var_small = Variance::from_variance(2f64.powf(-90.0));
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut encryption_generator =
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
///
/// let mut lwe_tpksk = LweTracePackingKeyswitchKey::new(
/// 0u64,
/// decomp_base_log,
/// decomp_level_count,
/// lwe_dimension.to_lwe_size(),
/// glwe_dimension.to_glwe_size(),
/// polynomial_size,
/// ciphertext_modulus,
/// );
///
/// generate_lwe_trace_packing_keyswitch_key(
/// &glwe_secret_key,
/// &mut lwe_tpksk,
/// var_small,
/// &mut encryption_generator,
/// );
///
/// let mut lwe_ctxt_list = LweCiphertextList::new(
/// 0u64,
/// lwe_dimension.to_lwe_size(),
/// lwe_count,
/// ciphertext_modulus,
/// );
///
/// let msg = 1u64;
/// let plaintext_list = PlaintextList::new(msg << 56, PlaintextCount(lwe_count.0));
///
/// encrypt_lwe_ciphertext_list(
/// &lwe_secret_key,
/// &mut lwe_ctxt_list,
/// &plaintext_list,
/// lwe_modular_std_dev,
/// &mut encryption_generator,
/// );
///
/// let mut output_glwe_ciphertext = GlweCiphertext::new(
/// 0u64,
/// glwe_dimension.to_glwe_size(),
/// polynomial_size,
/// ciphertext_modulus,
/// );
///
/// let mut indices = vec![0_usize; lwe_count.0];
/// for (index, item) in indices.iter_mut().enumerate() {
/// *item = index;
/// }
///
/// trace_packing_keyswitch_lwe_ciphertext_list_into_glwe_ciphertext(
/// &lwe_tpksk,
/// &mut output_glwe_ciphertext,
/// &lwe_ctxt_list,
/// indices,
/// );
///
/// let mut output_plaintext_list = PlaintextList::new(0u64, PlaintextCount(polynomial_size.0));
///
/// decrypt_glwe_ciphertext(
/// &glwe_secret_key,
/// &output_glwe_ciphertext,
/// &mut output_plaintext_list,
/// );
///
/// // Round and remove encoding
/// // First create a decomposer working on the high 8 bits corresponding to our encoding.
/// let decomposer = SignedDecomposer::new(DecompositionBaseLog(8), DecompositionLevelCount(1));
///
/// output_plaintext_list
/// .iter_mut()
/// .for_each(|elt| *elt.0 = decomposer.closest_representable(*elt.0));
///
/// // Get the raw vector
/// let mut cleartext_list = output_plaintext_list.into_container();
/// // Remove the encoding
/// // Due to not having an inverse of 2 the packing multiplies each value by polynomial_size
/// // Hence the encoding delta is multiplied by polynomial_size
/// cleartext_list
/// .iter_mut()
/// .for_each(|elt| *elt = *elt >> 56 + polynomial_size.log2().0);
/// // Get the list immutably
/// let cleartext_list = cleartext_list;
///
/// // Check we recovered the original message for each plaintext we encrypted
/// for (index, elt) in cleartext_list.iter().enumerate() {
/// if index < lwe_count.0 {
/// assert_eq!(*elt, msg);
/// } else {
/// assert_eq!(*elt, 0);
/// }
/// }
/// ```
pub fn trace_packing_keyswitch_lwe_ciphertext_list_into_glwe_ciphertext<
Scalar,
KeyCont,
InputCont,
OutputCont,
>(
lwe_tpksk: &LweTracePackingKeyswitchKey<KeyCont>,
output_glwe_ciphertext: &mut GlweCiphertext<OutputCont>,
input_lwe_ciphertext_list: &LweCiphertextList<InputCont>,
indices: Vec<usize>,
) where
Scalar: UnsignedTorus,
KeyCont: Container<Element = Scalar>,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
{
assert!(
input_lwe_ciphertext_list.lwe_ciphertext_count().0
<= output_glwe_ciphertext.polynomial_size().0
);
assert_eq!(
input_lwe_ciphertext_list.lwe_ciphertext_count().0,
indices.len()
);
assert_eq!(
input_lwe_ciphertext_list.lwe_size(),
lwe_tpksk.input_lwe_size()
);
assert!(indices
.iter()
.all(|&x| x < output_glwe_ciphertext.polynomial_size().0));
assert_eq!(
output_glwe_ciphertext.polynomial_size(),
lwe_tpksk.polynomial_size()
);
assert_eq!(
output_glwe_ciphertext.glwe_size(),
lwe_tpksk.output_glwe_size()
);
assert_eq!(
input_lwe_ciphertext_list.ciphertext_modulus(),
lwe_tpksk.ciphertext_modulus()
);
assert_eq!(
output_glwe_ciphertext.ciphertext_modulus(),
lwe_tpksk.ciphertext_modulus()
);
// We reset the output
output_glwe_ciphertext.as_mut().fill(Scalar::ZERO);
let poly_size = output_glwe_ciphertext.polynomial_size();
let glwe_count = GlweCiphertextCount(poly_size.0);
let ciphertext_modulus = output_glwe_ciphertext.ciphertext_modulus();
let mut glwe_list = GlweCiphertextList::new(
Scalar::ZERO,
output_glwe_ciphertext.glwe_size(),
poly_size,
glwe_count,
ciphertext_modulus,
);
// Construct the initial Glwe Ciphertexts
for (index1, mut glwe_ct) in glwe_list.iter_mut().enumerate() {
for (index2, index) in indices.iter().enumerate() {
if index1 == *index {
let lwe_ct = input_lwe_ciphertext_list.get(index2);
let lwe_body = lwe_ct.as_ref().last().unwrap();
let lwe_mask = lwe_ct.get_mask();
for (index3, mut ring_element) in glwe_ct
.get_mut_mask()
.as_mut_polynomial_list()
.iter_mut()
.enumerate()
{
for (index4, coef) in ring_element.iter_mut().enumerate() {
if index3 * poly_size.0 + index4 < lwe_mask.lwe_dimension().0 {
*coef =
coef.wrapping_add(lwe_mask.as_ref()[index3 * poly_size.0 + index4]);
}
}
}
let mut poly_to_add = Polynomial::new(Scalar::ZERO, poly_size);
poly_to_add[0] = poly_to_add[0].wrapping_add(*lwe_body);
polynomial_wrapping_add_assign(
&mut glwe_ct.get_mut_body().as_mut_polynomial(),
&poly_to_add,
);
}
}
}
for l in 0..poly_size.log2().0 {
for i in 0..(poly_size.0 / 2_usize.pow(l as u32 + 1)) {
let ct_0 = glwe_list.get(i);
let glwe_size = ct_0.glwe_size();
let j = (poly_size.0 / 2_usize.pow(l as u32 + 1)) + i;
let ct_1 = glwe_list.get(j);
if ct_0.as_ref().iter().any(|&x| x != Scalar::ZERO)
|| ct_1.as_ref().iter().any(|&x| x != Scalar::ZERO)
{
// Rotate ct_1 by N/2^(l+1)
for mut pol in glwe_list.get_mut(j).as_mut_polynomial_list().iter_mut() {
polynomial_wrapping_monic_monomial_mul_assign(
&mut pol,
MonomialDegree(poly_size.0 / 2_usize.pow(l as u32 + 1)),
);
}
let mut ct_plus =
GlweCiphertext::new(Scalar::ZERO, glwe_size, poly_size, ciphertext_modulus);
let mut ct_minus =
GlweCiphertext::new(Scalar::ZERO, glwe_size, poly_size, ciphertext_modulus);
for ((mut pol_plus, pol_0), pol_1) in ct_plus
.as_mut_polynomial_list()
.iter_mut()
.zip(glwe_list.get(i).as_polynomial_list().iter())
.zip(glwe_list.get(j).as_polynomial_list().iter())
{
polynomial_wrapping_add_assign(&mut pol_plus, &pol_0);
polynomial_wrapping_add_assign(&mut pol_plus, &pol_1);
}
for ((mut pol_minus, pol_0), pol_1) in ct_minus
.as_mut_polynomial_list()
.iter_mut()
.zip(glwe_list.get(i).as_polynomial_list().iter())
.zip(glwe_list.get(j).as_polynomial_list().iter())
{
polynomial_wrapping_add_assign(&mut pol_minus, &pol_0);
polynomial_wrapping_sub_assign(&mut pol_minus, &pol_1);
}
// Now we should scale both ct_plus and ct_minus by 2^-1 mod q = (q+1) / 2 for odd q
let scalar = Scalar::ONE; // change to (q + 1)/2 for odd q
if !ciphertext_modulus.is_power_of_two() {
// Set scalar to (q + 1)/2
}
for mut pol in ct_plus.as_mut_polynomial_list().iter_mut() {
polynomial_wrapping_scalar_mul_assign(&mut pol, scalar);
}
for mut pol in ct_minus.as_mut_polynomial_list().iter_mut() {
polynomial_wrapping_scalar_mul_assign(&mut pol, scalar);
}
// Apply the automorphism sending X to X^(2^(l+1) + 1) to ct_minus
for mut pol in ct_minus.as_mut_polynomial_list().iter_mut() {
apply_automorphism_assign(&mut pol, 2_usize.pow(l as u32 + 1) + 1)
}
let mut ks_out = GlweCiphertext::new(
Scalar::ZERO,
ct_minus.glwe_size(),
poly_size,
ciphertext_modulus,
);
let glwe_ksk = GlweKeyswitchKey::from_container(
lwe_tpksk.get(l).into_container(),
lwe_tpksk.decomposition_base_log(),
lwe_tpksk.decomposition_level_count(),
lwe_tpksk.output_glwe_size(),
lwe_tpksk.polynomial_size(),
lwe_tpksk.ciphertext_modulus(),
);
// Perform a Glwe keyswitch on ct_minus
keyswitch_glwe_ciphertext(&glwe_ksk, &mut ct_minus, &mut ks_out);
// Set ct_0 to zero
glwe_list.get_mut(i).as_mut().fill(Scalar::ZERO);
// Add the result to ct_plus and add this to ct_0
for ((mut pol_plus, pol_ks), mut pol_0) in ct_plus
.as_mut_polynomial_list()
.iter_mut()
.zip(ks_out.as_polynomial_list().iter())
.zip(glwe_list.get_mut(i).as_mut_polynomial_list().iter_mut())
{
polynomial_wrapping_add_assign(&mut pol_plus, &pol_ks);
polynomial_wrapping_add_assign(&mut pol_0, &pol_plus);
}
}
}
}
let res = glwe_list.get(0);
output_glwe_ciphertext.as_mut().fill(Scalar::ZERO);
for (mut pol_out, pol_res) in output_glwe_ciphertext
.as_mut_polynomial_list()
.iter_mut()
.zip(res.as_polynomial_list().iter())
{
polynomial_wrapping_add_assign(&mut pol_out, &pol_res);
}
}

View File

@@ -0,0 +1,176 @@
//! Module containing primitives pertaining to [`LWE trace packing keyswitch key
//! generation`](`LweTracePackingKeyswitchKey`).
use crate::core_crypto::algorithms::*;
use crate::core_crypto::commons::dispersion::DispersionParameter;
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
use crate::core_crypto::prelude::polynomial_algorithms::apply_automorphism_wrapping_add_assign;
/// Fill a [`GLWE secret key`](`GlweSecretKey`) with an actual key derived from an
/// [`LWE secret key`](`LweSecretKey`) for use in the [`LWE trace packing keyswitch key`]
/// (`LweTracePackingKeyswitchKey`)
/// # Example
///
/// ```
/// use tfhe::core_crypto::prelude::*;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for GlweCiphertext creation
/// let glwe_size = GlweSize(3);
/// let polynomial_size = PolynomialSize(1024);
/// let lwe_dimension = LweDimension(900);
/// let ciphertext_modulus = CiphertextModulus::new_native();
///
/// let mut seeder = new_seeder();
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
/// let lwe_secret_key =
/// allocate_and_generate_new_binary_lwe_secret_key(lwe_dimension, &mut secret_generator);
///
/// let mut glwe_secret_key =
/// GlweSecretKey::new_empty_key(0u64, glwe_size.to_glwe_dimension(), polynomial_size);
///
/// generate_tpksk_output_glwe_secret_key(&lwe_secret_key, &mut glwe_secret_key);
///
/// let decomp_base_log = DecompositionBaseLog(2);
/// let decomp_level_count = DecompositionLevelCount(8);
/// let var_small = Variance::from_variance(2f64.powf(-80.0));
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut encryption_generator =
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
///
/// let mut lwe_tpksk = LweTracePackingKeyswitchKey::new(
/// 0u64,
/// decomp_base_log,
/// decomp_level_count,
/// lwe_dimension.to_lwe_size(),
/// glwe_size,
/// polynomial_size,
/// ciphertext_modulus,
/// );
///
/// generate_lwe_trace_packing_keyswitch_key(
/// &glwe_secret_key,
/// &mut lwe_tpksk,
/// var_small,
/// &mut encryption_generator,
/// );
/// ```
pub fn generate_tpksk_output_glwe_secret_key<Scalar, InputKeyCont, OutputKeyCont>(
input_lwe_secret_key: &LweSecretKey<InputKeyCont>,
output_glwe_secret_key: &mut GlweSecretKey<OutputKeyCont>,
) where
Scalar: UnsignedTorus,
InputKeyCont: Container<Element = Scalar>,
OutputKeyCont: ContainerMut<Element = Scalar>,
{
let lwe_dimension = input_lwe_secret_key.lwe_dimension();
let glwe_dimension = output_glwe_secret_key.glwe_dimension();
let glwe_poly_size = output_glwe_secret_key.polynomial_size();
assert!(
lwe_dimension.0 <= glwe_dimension.0 * glwe_poly_size.0,
"Mismatched between input_lwe_secret_key dimension {:?} and number of coefficients of \
output_glwe_secret_key {:?}.",
lwe_dimension.0,
glwe_dimension.0 * glwe_poly_size.0
);
let glwe_key_container = output_glwe_secret_key.as_mut();
for (index, lwe_key_bit) in input_lwe_secret_key.as_ref().iter().enumerate() {
if index % glwe_poly_size.0 == 0 {
glwe_key_container[index] = *lwe_key_bit;
} else {
let rem = index % glwe_poly_size.0;
let quo = index / glwe_poly_size.0;
let new_index = (quo + 1) * glwe_poly_size.0 - rem;
glwe_key_container[new_index] = Scalar::ZERO.wrapping_sub(*lwe_key_bit);
}
}
}
/// Fill an [`LWE trace packing keyswitch key`](`LweTracePackingKeyswitchKey`)
/// with an actual key.
pub fn generate_lwe_trace_packing_keyswitch_key<Scalar, InputKeyCont, KSKeyCont, Gen>(
input_glwe_secret_key: &GlweSecretKey<InputKeyCont>,
lwe_tpksk: &mut LweTracePackingKeyswitchKey<KSKeyCont>,
noise_parameters: impl DispersionParameter,
generator: &mut EncryptionRandomGenerator<Gen>,
) where
Scalar: UnsignedTorus,
InputKeyCont: Container<Element = Scalar>,
KSKeyCont: ContainerMut<Element = Scalar>,
Gen: ByteRandomGenerator,
{
assert_eq!(
input_glwe_secret_key.glwe_dimension(),
lwe_tpksk.output_glwe_key_dimension()
);
assert_eq!(
input_glwe_secret_key.polynomial_size(),
lwe_tpksk.polynomial_size()
);
// We retrieve decomposition arguments
let glwe_dimension = lwe_tpksk.output_glwe_key_dimension();
let decomp_level_count = lwe_tpksk.decomposition_level_count();
let decomp_base_log = lwe_tpksk.decomposition_base_log();
let polynomial_size = lwe_tpksk.polynomial_size();
let ciphertext_modulus = lwe_tpksk.ciphertext_modulus();
let automorphism_index_iter = 1..=polynomial_size.log2().0;
let gen_iter = generator
.fork_tpksk_to_tpksk_chunks::<Scalar>(
decomp_level_count,
glwe_dimension.to_glwe_size(),
polynomial_size,
)
.unwrap();
// loop over the before key blocks
for ((auto_index, glwe_keyswitch_block), mut loop_generator) in automorphism_index_iter
.zip(lwe_tpksk.iter_mut())
.zip(gen_iter)
{
let mut auto_glwe_sk = GlweSecretKey::new_empty_key(
Scalar::ZERO,
input_glwe_secret_key.glwe_dimension(),
input_glwe_secret_key.polynomial_size(),
);
let input_key_block_iter = input_glwe_secret_key
.as_ref()
.chunks_exact(polynomial_size.0);
let auto_key_block_iter = auto_glwe_sk.as_mut().chunks_exact_mut(polynomial_size.0);
for (auto_key_block, input_key_block) in auto_key_block_iter.zip(input_key_block_iter) {
let mut output_poly = Polynomial::from_container(auto_key_block);
let input_poly = Polynomial::from_container(input_key_block);
apply_automorphism_wrapping_add_assign(
&mut output_poly,
&input_poly,
2_usize.pow(auto_index as u32) + 1,
);
}
let mut glwe_ksk = GlweKeyswitchKey::from_container(
glwe_keyswitch_block.into_container(),
decomp_base_log,
decomp_level_count,
glwe_dimension.to_glwe_size(),
polynomial_size,
ciphertext_modulus,
);
generate_glwe_keyswitch_key(
&auto_glwe_sk,
input_glwe_secret_key,
&mut glwe_ksk,
noise_parameters,
&mut loop_generator,
);
}
}

View File

@@ -5,8 +5,12 @@
pub mod ggsw_conversion;
pub mod ggsw_encryption;
pub mod glwe_encryption;
pub mod glwe_keyswitch;
pub mod glwe_keyswitch_key_generation;
pub mod glwe_relinearisation_key_generation;
pub mod glwe_sample_extraction;
pub mod glwe_secret_key_generation;
pub mod glwe_tensor_product;
pub mod lwe_bootstrap_key_conversion;
pub mod lwe_bootstrap_key_generation;
pub mod lwe_encryption;
@@ -19,8 +23,12 @@ pub mod lwe_multi_bit_programmable_bootstrapping;
pub mod lwe_private_functional_packing_keyswitch;
pub mod lwe_private_functional_packing_keyswitch_key_generation;
pub mod lwe_programmable_bootstrapping;
pub mod lwe_public_functional_packing_keyswitch;
pub mod lwe_public_functional_packing_keyswitch_key_generation;
pub mod lwe_public_key_generation;
pub mod lwe_secret_key_generation;
pub mod lwe_trace_packing_keyswitch;
pub mod lwe_trace_packing_keyswitch_key_generation;
pub mod lwe_wopbs;
pub mod polynomial_algorithms;
pub mod seeded_ggsw_ciphertext_decompression;
@@ -42,8 +50,12 @@ mod test;
pub use ggsw_conversion::*;
pub use ggsw_encryption::*;
pub use glwe_encryption::*;
pub use glwe_keyswitch::*;
pub use glwe_keyswitch_key_generation::*;
pub use glwe_relinearisation_key_generation::*;
pub use glwe_sample_extraction::*;
pub use glwe_secret_key_generation::*;
pub use glwe_tensor_product::*;
pub use lwe_bootstrap_key_conversion::*;
pub use lwe_bootstrap_key_generation::*;
pub use lwe_encryption::*;
@@ -56,8 +68,12 @@ pub use lwe_multi_bit_programmable_bootstrapping::*;
pub use lwe_private_functional_packing_keyswitch::*;
pub use lwe_private_functional_packing_keyswitch_key_generation::*;
pub use lwe_programmable_bootstrapping::*;
pub use lwe_public_functional_packing_keyswitch::*;
pub use lwe_public_functional_packing_keyswitch_key_generation::*;
pub use lwe_public_key_generation::*;
pub use lwe_secret_key_generation::*;
pub use lwe_trace_packing_keyswitch::*;
pub use lwe_trace_packing_keyswitch_key_generation::*;
pub use lwe_wopbs::*;
pub use seeded_ggsw_ciphertext_decompression::*;
pub use seeded_ggsw_ciphertext_list_decompression::*;

View File

@@ -406,6 +406,33 @@ pub fn polynomial_wrapping_mul<Scalar, OutputCont, LhsCont, RhsCont>(
polynomial_wrapping_add_mul_assign(output, lhs, rhs);
}
/// Multiply a polynomial by a scalar.
///
/// # Note
///
/// Computations wrap around (similar to computing modulo $2^{n\_{bits}}$) when exceeding the
/// unsigned integer capacity.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::algorithms::polynomial_algorithms::*;
/// use tfhe::core_crypto::entities::*;
/// let mut pol = Polynomial::from_container(vec![1u8, 2, 3, 4, 5, 6]);
/// let scalar = 127u8;
/// polynomial_wrapping_scalar_mul_assign(&mut pol, scalar);
/// assert_eq!(pol.as_ref(), &[127u8, 254, 125, 252, 123, 250]);
/// ```
pub fn polynomial_wrapping_scalar_mul_assign<Scalar, PolyCont>(
output: &mut Polynomial<PolyCont>,
scalar: Scalar,
) where
Scalar: UnsignedInteger,
PolyCont: ContainerMut<Element = Scalar>,
{
slice_wrapping_scalar_mul_assign(output, scalar)
}
/// Fill the output polynomial, with the result of the product of two polynomials, reduced modulo
/// $(X^{N} + 1)$ with the Karatsuba algorithm Complexity: $O(N^{1.58})$
///
@@ -530,6 +557,70 @@ where
}
}
///
pub fn apply_automorphism_wrapping_add_assign<Scalar, OutputCont, PolyCont>(
output: &mut Polynomial<OutputCont>,
input: &Polynomial<PolyCont>,
automorphism_exponent: usize,
) where
Scalar: UnsignedInteger,
OutputCont: ContainerMut<Element = Scalar>,
PolyCont: Container<Element = Scalar>,
{
// check input and output polynomials have the same size
assert_eq!(input.polynomial_size(), output.polynomial_size());
// check the automorphism exponent is odd so the function X -> X^automorphism_exponent is an
// automorphism (assumes polysize is a power of 2)
assert_eq!(automorphism_exponent % 2, 1);
let poly_size = input.polynomial_size().0;
for (index, coef) in input.iter().enumerate() {
let new_index = (index * automorphism_exponent) % poly_size;
if (index * automorphism_exponent) % (2 * poly_size) == new_index {
output[new_index] = output[new_index].wrapping_add(*coef);
} else {
output[new_index] = output[new_index].wrapping_sub(*coef);
}
}
}
pub fn apply_automorphism_assign<Scalar, PolyCont>(
input: &mut Polynomial<PolyCont>,
automorphism_exponent: usize,
) where
Scalar: UnsignedInteger,
PolyCont: ContainerMut<Element = Scalar>,
{
let mut temp = Polynomial::new(Scalar::ZERO, input.polynomial_size());
apply_automorphism_wrapping_add_assign(&mut temp, input, automorphism_exponent);
input.fill(Scalar::ZERO);
polynomial_wrapping_add_assign(input, &temp);
}
pub fn polynomial_list_wrapping_sub_scalar_mul_assign<Scalar, InputCont, OutputCont, PolyCont>(
output_poly_list: &mut PolynomialList<OutputCont>,
input_poly_list: &PolynomialList<InputCont>,
scalar_poly: &Polynomial<PolyCont>,
) where
Scalar: UnsignedInteger,
OutputCont: ContainerMut<Element = Scalar>,
InputCont: Container<Element = Scalar>,
PolyCont: Container<Element = Scalar>,
{
assert_eq!(
output_poly_list.polynomial_size(),
input_poly_list.polynomial_size()
);
assert_eq!(
output_poly_list.polynomial_count(),
input_poly_list.polynomial_count()
);
for (mut output_poly, input_poly) in output_poly_list.iter_mut().zip(input_poly_list.iter()) {
polynomial_wrapping_sub_mul_assign(&mut output_poly, &input_poly, scalar_poly)
}
}
#[cfg(test)]
mod test {
use rand::Rng;

View File

@@ -169,6 +169,31 @@ impl<G: ByteRandomGenerator> EncryptionRandomGenerator<G> {
self.try_fork(lwe_size.0, mask_bytes, noise_bytes)
}
// Forks the generator, when splitting a tpksk into chunks
pub(crate) fn fork_tpksk_to_tpksk_chunks<T: UnsignedInteger>(
&mut self,
level: DecompositionLevelCount,
glwe_size: GlweSize,
poly_size: PolynomialSize,
) -> Result<impl Iterator<Item = EncryptionRandomGenerator<G>>, ForkError> {
let mask_bytes = mask_bytes_per_tpksk_chunk::<T>(level, glwe_size, poly_size);
let noise_bytes = noise_bytes_per_tpksk_chunk(level, poly_size);
self.try_fork(poly_size.log2().0, mask_bytes, noise_bytes)
}
// Forks the generator, when splitting a glwe keyswitch into chunks
pub(crate) fn fork_glweks_to_glweks_chunks<T: UnsignedInteger>(
&mut self,
level: DecompositionLevelCount,
input_glwe_dimension: GlweDimension,
output_glwe_size: GlweSize,
poly_size: PolynomialSize,
) -> Result<impl Iterator<Item = EncryptionRandomGenerator<G>>, ForkError> {
let mask_bytes = mask_bytes_per_glweks_chunk::<T>(level, output_glwe_size, poly_size);
let noise_bytes = noise_bytes_per_glweks_chunk(level, poly_size);
self.try_fork(input_glwe_dimension.0, mask_bytes, noise_bytes)
}
// Forks both generators into an iterator
fn try_fork(
&mut self,
@@ -431,6 +456,22 @@ impl<G: ParallelByteRandomGenerator> EncryptionRandomGenerator<G> {
self.par_try_fork(lwe_size.0, mask_bytes, noise_bytes)
}
// Forks the generator, when splitting a tpksk into chunks
pub(crate) fn par_fork_tpksk_to_tpksk_chunks<T: UnsignedInteger>(
&mut self,
level: DecompositionLevelCount,
glwe_size: GlweSize,
poly_size: PolynomialSize,
) -> Result<impl IndexedParallelIterator<Item = EncryptionRandomGenerator<G>>, ForkError> {
let mask_bytes = mask_bytes_per_tpksk_chunk::<T>(level, glwe_size, poly_size);
let noise_bytes = noise_bytes_per_tpksk_chunk(level, poly_size);
self.par_try_fork(
poly_size.log2().0 * glwe_size.to_glwe_dimension().0,
mask_bytes,
noise_bytes,
)
}
// Forks both generators into a parallel iterator.
fn par_try_fork(
&mut self,
@@ -504,6 +545,32 @@ fn mask_bytes_per_pfpksk<T: UnsignedInteger>(
lwe_size.0 * mask_bytes_per_pfpksk_chunk::<T>(level, glwe_size, poly_size)
}
fn mask_bytes_per_tpksk_chunk<T: UnsignedInteger>(
level: DecompositionLevelCount,
glwe_size: GlweSize,
poly_size: PolynomialSize,
) -> usize {
glwe_size.to_glwe_dimension().0 * mask_bytes_per_glweks_chunk::<T>(level, glwe_size, poly_size)
}
fn mask_bytes_per_tpksk<T: UnsignedInteger>(
level: DecompositionLevelCount,
glwe_size: GlweSize,
poly_size: PolynomialSize,
) -> usize {
poly_size.log2().0 * mask_bytes_per_tpksk_chunk::<T>(level, glwe_size, poly_size)
}
fn mask_bytes_per_glweks_chunk<T: UnsignedInteger>(
level: DecompositionLevelCount,
glwe_size: GlweSize,
poly_size: PolynomialSize,
) -> usize {
glwe_size.to_glwe_dimension().0
* level.0
* mask_bytes_per_glwe::<T>(glwe_size.to_glwe_dimension(), poly_size)
}
fn noise_bytes_per_coef() -> usize {
// We use f64 to sample the noise for every precision, and we need 4/pi inputs to generate
// such an output (here we take 32 to keep a safety margin).
@@ -553,6 +620,27 @@ fn noise_bytes_per_pfpksk(
lwe_size.0 * noise_bytes_per_pfpksk_chunk(level, poly_size)
}
fn noise_bytes_per_tpksk_chunk(level: DecompositionLevelCount, poly_size: PolynomialSize) -> usize {
level.0 * noise_bytes_per_glwe(poly_size)
}
fn noise_bytes_per_tpksk(
level: DecompositionLevelCount,
poly_size: PolynomialSize,
glwe_size: GlweSize,
) -> usize {
glwe_size.to_glwe_dimension().0
* poly_size.log2().0
* noise_bytes_per_tpksk_chunk(level, poly_size)
}
fn noise_bytes_per_glweks_chunk(
level: DecompositionLevelCount,
poly_size: PolynomialSize,
) -> usize {
level.0 * noise_bytes_per_glwe(poly_size)
}
#[cfg(test)]
mod test {
use crate::core_crypto::algorithms::*;

View File

@@ -1,4 +1,6 @@
use crate::core_crypto::commons::math::decomposition::SignedDecompositionIter;
use crate::core_crypto::commons::math::decomposition::{
SignedDecompositionIter, SliceSignedDecompositionIter,
};
use crate::core_crypto::commons::numeric::{Numeric, UnsignedInteger};
use crate::core_crypto::commons::parameters::{DecompositionBaseLog, DecompositionLevelCount};
use std::marker::PhantomData;
@@ -113,6 +115,29 @@ where
res << non_rep_bit_count
}
/// Fills a mutable tensor-like objects with the closest representable values from another
/// tensor-like object.
///
/// # Example
///
/// ```rust
/// use tfhe::core_crypto::commons::math::decomposition::SignedDecomposer;
/// use tfhe::core_crypto::prelude::{DecompositionBaseLog, DecompositionLevelCount};
/// let decomposer =
/// SignedDecomposer::<u32>::new(DecompositionBaseLog(4), DecompositionLevelCount(3));
///
/// let input = vec![1_340_987_234_u32; 2];
/// let mut closest = vec![0u32; 2];
/// decomposer.fill_slice_with_closest_representable(&mut closest, &input);
/// assert!(closest.iter().all(|&x| x == 1_341_128_704_u32));
/// ```
pub fn fill_slice_with_closest_representable(&self, output: &mut [Scalar], input: &[Scalar]) {
output
.iter_mut()
.zip(input.iter())
.for_each(|(dst, &src)| *dst = self.closest_representable(src));
}
/// Generate an iterator over the terms of the decomposition of the input.
///
/// # Warning
@@ -173,4 +198,87 @@ where
None
}
}
/// Generates an iterator-like object over tensors of terms of the decomposition of the input
/// tensor.
///
/// # Warning
///
/// The returned iterator yields the terms $(\tilde{\theta}^{(a)}\_i)\_{a\in\mathbb{N}}$ in
/// order of decreasing $i$.
///
/// # Example
///
/// ```rust
/// use tfhe::core_crypto::commons::math::decomposition::SignedDecomposer;
/// use tfhe::core_crypto::commons::numeric::UnsignedInteger;
/// use tfhe::core_crypto::prelude::{DecompositionBaseLog, DecompositionLevelCount};
/// let decomposer =
/// SignedDecomposer::<u32>::new(DecompositionBaseLog(4), DecompositionLevelCount(3));
/// let decomposable = vec![1_340_987_234_u32, 1_340_987_234_u32];
/// let mut decomp = decomposer.decompose_slice(&decomposable);
///
/// let mut count = 0;
/// while let Some(term) = decomp.next_term() {
/// assert!(1 <= term.level().0);
/// assert!(term.level().0 <= 3);
/// for elmt in term.as_slice().iter() {
/// let signed_term = elmt.into_signed();
/// let half_basis = 2i32.pow(4) / 2i32;
/// assert!(-half_basis <= signed_term);
/// assert!(signed_term < half_basis);
/// }
/// count += 1;
/// }
/// assert_eq!(count, 3);
/// ```
pub fn decompose_slice(&self, input: &[Scalar]) -> SliceSignedDecompositionIter<Scalar> {
// Note that there would be no sense of making the decomposition on an input which was
// not rounded to the closest representable first. We then perform it before decomposing.
let mut rounded = vec![Scalar::ZERO; input.len()];
self.fill_slice_with_closest_representable(&mut rounded, input);
SliceSignedDecompositionIter::new(
&rounded,
DecompositionBaseLog(self.base_log),
DecompositionLevelCount(self.level_count),
)
}
/// Fills the output tensor with the recomposition of an other tensor.
///
/// Returns `Some(())` if the decomposition was fresh, and the output was filled with a
/// recomposition, and `None`, if not.
///
/// # Example
///
/// ```rust
/// use tfhe::core_crypto::commons::math::decomposition::SignedDecomposer;
/// use tfhe::core_crypto::prelude::{DecompositionBaseLog, DecompositionLevelCount};
/// let decomposer =
/// SignedDecomposer::<u32>::new(DecompositionBaseLog(4), DecompositionLevelCount(3));
/// let decomposable = vec![1_340_987_234_u32; 2];
/// let mut rounded = vec![0u32; 2];
/// decomposer.fill_slice_with_closest_representable(&mut rounded, &decomposable);
/// let mut decomp = decomposer.decompose_slice(&rounded);
/// let mut recomposition = vec![0u32; 2];
/// decomposer
/// .fill_slice_with_recompose(decomp, &mut recomposition)
/// .unwrap();
/// assert_eq!(recomposition, rounded);
/// ```
pub fn fill_slice_with_recompose(
&self,
decomp: SliceSignedDecompositionIter<Scalar>,
output: &mut [Scalar],
) -> Option<()> {
let mut decomp = decomp;
if decomp.is_fresh() {
while let Some(term) = decomp.next_term() {
term.update_slice_with_recomposition_summand_wrapping_addition(output);
}
Some(())
} else {
None
}
}
}

View File

@@ -1,4 +1,6 @@
use crate::core_crypto::commons::math::decomposition::{DecompositionLevel, DecompositionTerm};
use crate::core_crypto::commons::math::decomposition::{
DecompositionLevel, DecompositionTerm, DecompositionTermSlice,
};
use crate::core_crypto::commons::numeric::UnsignedInteger;
use crate::core_crypto::commons::parameters::{DecompositionBaseLog, DecompositionLevelCount};
@@ -114,6 +116,155 @@ where
}
}
/// An iterator-like object that yields the terms of the signed decomposition of a tensor of values.
///
/// # Note
///
/// On each call to [`SliceSignedDecompositionIter::next_term`], this structure yields a new
/// [`DecompositionTermSlice`], backed by a `Vec` owned by the structure. This vec is mutated at
/// each call of the `next_term` method, and as such the term must be dropped before `next_term` is
/// called again.
///
/// Such a pattern can not be implemented with iterators yet (without GATs), which is why this
/// iterator must be explicitly called.
///
/// # Warning
///
/// This iterator yields the decomposition in reverse order. That means that the highest level
/// will be yielded first.
pub struct SliceSignedDecompositionIter<Scalar>
where
Scalar: UnsignedInteger,
{
// The base log of the decomposition
base_log: usize,
// The number of levels of the decomposition
level_count: usize,
// The current level
current_level: usize,
// A mask which allows to compute the mod B of a value. For B=2^4, this guy is of the form:
// ...0001111
mod_b_mask: Scalar,
// The values being decomposed
inputs: Vec<Scalar>,
// The internal states of each decomposition
states: Vec<Scalar>,
// In order to avoid allocating a new Vec every time we yield a decomposition term, we store
// a Vec inside the structure and yield slices pointing to it.
outputs: Vec<Scalar>,
// A flag which stores whether the iterator is a fresh one (for the recompose method).
fresh: bool,
}
impl<Scalar> SliceSignedDecompositionIter<Scalar>
where
Scalar: UnsignedInteger,
{
// Creates a new tensor decomposition iterator.
pub(crate) fn new(
input: &[Scalar],
base_log: DecompositionBaseLog,
level: DecompositionLevelCount,
) -> SliceSignedDecompositionIter<Scalar> {
let len = input.len();
SliceSignedDecompositionIter {
base_log: base_log.0,
level_count: level.0,
current_level: level.0,
mod_b_mask: (Scalar::ONE << base_log.0) - Scalar::ONE,
inputs: input.to_vec(),
outputs: vec![Scalar::ZERO; len],
states: input
.iter()
.map(|i| *i >> (Scalar::BITS - base_log.0 * level.0))
.collect(),
fresh: true,
}
}
pub(crate) fn is_fresh(&self) -> bool {
self.fresh
}
/// Returns the logarithm in base two of the base of this decomposition.
///
/// If the decomposition uses a base $B=2^b$, this returns $b$.
///
/// # Example
///
/// ```rust
/// use tfhe::core_crypto::commons::math::decomposition::SignedDecomposer;
/// use tfhe::core_crypto::prelude::{DecompositionBaseLog, DecompositionLevelCount};
/// let decomposer =
/// SignedDecomposer::<u32>::new(DecompositionBaseLog(4), DecompositionLevelCount(3));
/// let decomposable = vec![1_340_987_234_u32; 2];
/// let decomp = decomposer.decompose_slice(&decomposable);
/// assert_eq!(decomp.base_log(), DecompositionBaseLog(4));
/// ```
pub fn base_log(&self) -> DecompositionBaseLog {
DecompositionBaseLog(self.base_log)
}
/// Returns the number of levels of this decomposition.
///
/// If the decomposition uses $l$ levels, this returns $l$.
///
/// # Example
///
/// ```rust
/// use tfhe::core_crypto::commons::math::decomposition::SignedDecomposer;
/// use tfhe::core_crypto::prelude::{DecompositionBaseLog, DecompositionLevelCount};
/// let decomposer =
/// SignedDecomposer::<u32>::new(DecompositionBaseLog(4), DecompositionLevelCount(3));
/// let decomposable = vec![1_340_987_234_u32; 2];
/// let decomp = decomposer.decompose_slice(&decomposable);
/// assert_eq!(decomp.level_count(), DecompositionLevelCount(3));
/// ```
pub fn level_count(&self) -> DecompositionLevelCount {
DecompositionLevelCount(self.level_count)
}
/// Yield the next term of the decomposition, if any.
///
/// # Note
///
/// Because this function returns a borrowed tensor, owned by the iterator, the term must be
/// dropped before `next_term` is called again.
///
/// # Example
///
/// ```rust
/// use tfhe::core_crypto::commons::math::decomposition::{DecompositionLevel, SignedDecomposer};
/// use tfhe::core_crypto::prelude::{DecompositionBaseLog, DecompositionLevelCount};
/// let decomposer =
/// SignedDecomposer::<u32>::new(DecompositionBaseLog(4), DecompositionLevelCount(3));
/// let decomposable = vec![1_340_987_234_u32; 2];
/// let mut decomp = decomposer.decompose_slice(&decomposable);
/// let term = decomp.next_term().unwrap();
/// assert_eq!(term.level(), DecompositionLevel(3));
/// assert_eq!(term.as_slice()[0], 4294967295);
/// ```
pub fn next_term(&mut self) -> Option<DecompositionTermSlice<'_, Scalar>> {
// The iterator is not fresh anymore.
self.fresh = false;
// We check if the decomposition is over
if self.current_level == 0 {
return None;
}
// We iterate over the elements of the outputs and decompose
for (output_i, state_i) in self.outputs.iter_mut().zip(self.states.iter_mut()) {
*output_i = decompose_one_level(self.base_log, state_i, self.mod_b_mask);
}
self.current_level -= 1;
// We return the term tensor.
Some(DecompositionTermSlice::new(
DecompositionLevel(self.current_level + 1),
DecompositionBaseLog(self.base_log),
&self.outputs,
))
}
}
fn decompose_one_level<S: UnsignedInteger>(base_log: usize, state: &mut S, mod_b_mask: S) -> S {
let res = *state & mod_b_mask;
*state >>= base_log;

View File

@@ -91,3 +91,115 @@ where
DecompositionLevel(self.level)
}
}
/// A tensor whose elements are the terms of the decomposition of another tensor.
///
/// If we decompose each elements of a set of values $(\theta^{(a)})\_{a\in\mathbb{N}}$ as a set of
/// sums $(\sum\_{i=1}^l\tilde{\theta}^{(a)}\_i\frac{q}{B^i})\_{a\in\mathbb{N}}$, this represents a
/// set of $(\tilde{\theta}^{(a)}\_i)\_{a\in\mathbb{N}}$.
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct DecompositionTermSlice<'a, Scalar>
where
Scalar: UnsignedInteger,
{
level: usize,
base_log: usize,
slice: &'a [Scalar],
}
impl<'a, Scalar> DecompositionTermSlice<'a, Scalar>
where
Scalar: UnsignedInteger,
{
// Creates a new tensor decomposition term.
pub(crate) fn new(
level: DecompositionLevel,
base_log: DecompositionBaseLog,
slice: &'a [Scalar],
) -> DecompositionTermSlice<Scalar> {
DecompositionTermSlice {
level: level.0,
base_log: base_log.0,
slice,
}
}
/// Fills the output tensor with the terms turned to summands.
///
/// If our term tensor represents a set of $(\tilde{\theta}^{(a)}\_i)\_{a\in\mathbb{N}}$ of the
/// decomposition, this method fills the output tensor with a set of
/// $(\tilde{\theta}^{(a)}\_i\frac{q}{B^i})\_{a\in\mathbb{N}}$.
///
/// # Example
///
/// ```rust
/// use tfhe::core_crypto::commons::math::decomposition::SignedDecomposer;
/// use tfhe::core_crypto::prelude::{DecompositionBaseLog, DecompositionLevelCount};
/// let decomposer =
/// SignedDecomposer::<u32>::new(DecompositionBaseLog(4), DecompositionLevelCount(3));
/// let input = vec![2u32.pow(19); 2];
/// let mut decomp = decomposer.decompose_slice(&input);
/// let term = decomp.next_term().unwrap();
/// let mut output = vec![0, 2];
/// term.fill_slice_with_recomposition_summand(&mut output);
/// assert!(output.iter().all(|&x| x == 1048576));
/// ```
pub fn fill_slice_with_recomposition_summand(&self, output: &mut [Scalar]) {
output
.iter_mut()
.zip(self.slice.iter())
.for_each(|(dst, &value)| {
let shift: usize = <Scalar as Numeric>::BITS - self.base_log * self.level;
*dst = value << shift
});
}
pub(crate) fn update_slice_with_recomposition_summand_wrapping_addition(
&self,
output: &mut [Scalar],
) {
output
.iter_mut()
.zip(self.slice.iter())
.for_each(|(out, &value)| {
let shift: usize = <Scalar as Numeric>::BITS - self.base_log * self.level;
*out = (*out).wrapping_add(value << shift);
});
}
/// Returns a tensor with the values of term.
///
/// # Example
///
/// ```rust
/// use tfhe::core_crypto::commons::math::decomposition::SignedDecomposer;
/// use tfhe::core_crypto::prelude::{DecompositionBaseLog, DecompositionLevelCount};
/// let decomposer =
/// SignedDecomposer::<u32>::new(DecompositionBaseLog(4), DecompositionLevelCount(3));
/// let input = vec![2u32.pow(19); 2];
/// let mut decomp = decomposer.decompose_slice(&input);
/// let term = decomp.next_term().unwrap();
/// assert_eq!(term.as_slice()[0], 1);
/// ```
pub fn as_slice(&self) -> &'a [Scalar] {
self.slice
}
/// Returns the level of this decomposition term tensor.
///
/// # Example
///
/// ```rust
/// use tfhe::core_crypto::commons::math::decomposition::{DecompositionLevel, SignedDecomposer};
/// use tfhe::core_crypto::prelude::{DecompositionBaseLog, DecompositionLevelCount};
/// let decomposer =
/// SignedDecomposer::<u32>::new(DecompositionBaseLog(4), DecompositionLevelCount(3));
/// let input = vec![2u32.pow(19); 2];
/// let mut decomp = decomposer.decompose_slice(&input);
/// let term = decomp.next_term().unwrap();
/// assert_eq!(term.level(), DecompositionLevel(3));
/// ```
pub fn level(&self) -> DecompositionLevel {
DecompositionLevel(self.level)
}
}

View File

@@ -0,0 +1,428 @@
//! Module containing the definition of the GlweKeyswitchKey.
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
/// A [`GLWE keyswitch key`](`GlweKeyswitchKey`).
///
/// # Formal Definition
///
/// ## Key Switching Key
///
/// A key switching key is a vector of GLev ciphertexts (described on the bottom of
/// [`this page`](`crate::core_crypto::entities::GgswCiphertext#Glev-ciphertext`)).
/// It encrypts the coefficient of
/// the [`GLWE secret key`](`crate::core_crypto::entities::GlweSecretKey`)
/// $\vec{S}\_{\mathsf{in}}$ under the
/// [`GLWE secret key`](`crate::core_crypto::entities::GlweSecretKey`)
/// $\vec{S}\_{\mathsf{out}}$.
///
/// $$\mathsf{KSK}\_{\vec{S}\_{\mathsf{in}}\rightarrow \vec{S}\_{\mathsf{out}}} = \left(
/// \overline{\mathsf{CT}\_0}, \cdots , \overline{\mathsf{CT}\_{k\_{\mathsf{in}}-1}}\right)
/// \subseteq R\_q^{(k\_{\mathsf{out}}+1)\cdot k\_{\mathsf{in}}\cdot \ell}$$
///
/// where $\vec{S}\_{\mathsf{in}} = \left( S\_0 , \cdots , S\_{\mathsf{in}-1} \right)$ and for all
/// $0\le i <k\_{\mathsf{in}}$ we have $\overline{\mathsf{CT}\_i} \in
/// \mathsf{GLev}\_{\vec{S}\_{\mathsf{out}}}^{\beta, \ell}\left(S\_i\right)$.
///
/// ## GLWE Keyswitch
///
/// This homomorphic procedure transforms an input
/// [`GLWE ciphertext`](`crate::core_crypto::entities::GlweCiphertext`)
/// $\mathsf{CT}\_{\mathsf{in}} =
/// \left( \vec{A}\_{\mathsf{in}} , B\_{\mathsf{in}}\right) \in \mathsf{GLWE}^{k\_{\mathsf{in}}}\_
/// {\vec{S}\_{\mathsf{in}}}( \mathsf{PT} ) \subseteq R\_q^{(k\_{\mathsf{in}}+1)}$ into an
/// output [`GLWE ciphertext`](`crate::core_crypto::entities::GlweCiphertext`)
/// $\mathsf{CT}\_{\mathsf{out}} =
/// \left( \vec{A}\_{\mathsf{out}} , B\_{\mathsf{out}}\right) \in
/// \mathsf{GLWE}^{k\_{\mathsf{out}}}\_{\vec{S}\_{\mathsf{out}}}( \mathsf{PT} )\subseteq
/// R\_q^{(k\_{\mathsf{out}}+1)}$ where $k\_{\mathsf{in}} = |\vec{S}\_{\mathsf{in}}|$ and
/// $k\_{\mathsf{out}} = |\vec{S}\_{\mathsf{out}}|$. It requires a
/// [`key switching key`](`crate::core_crypto::entities::GlweKeyswitchKey`).
/// The input ciphertext is encrypted under the
/// [`GLWE secret key`](`crate::core_crypto::entities::GlweSecretKey`)
/// $\vec{S}\_{\mathsf{in}}$ and the output ciphertext is
/// encrypted under the [`GLWE secret key`](`crate::core_crypto::entities::GlweSecretKey`)
/// $\vec{S}\_{\mathsf{out}}$.
///
/// $$\mathsf{CT}\_{\mathsf{in}} \in \mathsf{GLWE}^{k\_{\mathsf{in}}}\_{\vec{S}\_{\mathsf{in}}}(
/// \mathsf{PT} ) ~~~~~~~~~~\mathsf{KSK}\_{\vec{S}\_{\mathsf{in}}\rightarrow
/// \vec{S}\_{\mathsf{out}}}$$ $$ \mathsf{keyswitch}\left(\mathsf{CT}\_{\mathsf{in}} , \mathsf{KSK}
/// \right) \rightarrow \mathsf{CT}\_{\mathsf{out}} \in
/// \mathsf{GLWE}^{k\_{\mathsf{out}}}\_{\vec{S}\_{\mathsf{out}}} \left( \mathsf{PT} \right)$$
///
/// ## Algorithm
/// ###### inputs:
/// - $\mathsf{CT}\_{\mathsf{in}} = \left( \vec{A}\_{\mathsf{in}} , B\_{\mathsf{in}}\right) \in
/// \mathsf{GLWE}^{k\_{\mathsf{in}}}\_{\vec{S}\_{\mathsf{in}}}( \mathsf{PT} )$: a [`GLWE
/// ciphertext`](`GlweCiphertext`) with $\vec{A}\_{\mathsf{in}}=\left(A\_0, \cdots
/// A\_{k\_{\mathsf{in}}-1}\right)$
/// - $\mathsf{KSK}\_{\vec{S}\_{\mathsf{in}}\rightarrow \vec{S}\_{\mathsf{out}}}$: a
/// [`key switching key`](`crate::core_crypto::entities::GlweKeyswitchKey`)
///
/// ###### outputs:
/// - $\mathsf{CT}\_{\mathsf{out}} \in \mathsf{GLWE}^{k\_{\mathsf{out}}}\_{\vec{S}\_{\mathsf{out}}}
/// \left( \mathsf{PT} \right)$: a
/// [`GLWE ciphertext`](`crate::core_crypto::entities::GlweCiphertext`)
///
/// ###### algorithm:
/// 1. set $\mathsf{cCT}=\left( 0 , \cdots , 0 , B\_{\mathsf{in}} \right) \in
/// R\_q^{(k\_{\mathsf{out}}+1)}$
/// 2. compute $\mathsf{CT}\_{\mathsf{out}} = \mathsf{CT} -
/// \sum\_{i=0}^{k\_{\mathsf{in}}-1} \mathsf{decompProduct}\left( A\_i , \overline{\mathsf{CT}\_i}
/// \right)$
/// 3. output $\mathsf{CT}\_{\mathsf{out}}$
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct GlweKeyswitchKey<C: Container>
where
C::Element: UnsignedInteger,
{
data: C,
decomp_base_log: DecompositionBaseLog,
decomp_level_count: DecompositionLevelCount,
output_glwe_size: GlweSize,
poly_size: PolynomialSize,
ciphertext_modulus: CiphertextModulus<C::Element>,
}
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]> for GlweKeyswitchKey<C> {
fn as_ref(&self) -> &[T] {
self.data.as_ref()
}
}
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]> for GlweKeyswitchKey<C> {
fn as_mut(&mut self) -> &mut [T] {
self.data.as_mut()
}
}
/// Return the number of elements in an encryption of an input [`GlweSecretKey`] element for a
/// [GlweKeyswitchKey`] given a [`DecompositionLevelCount`] and output [`GlweSize`].
pub fn glwe_keyswitch_key_input_key_element_encrypted_size(
decomp_level_count: DecompositionLevelCount,
output_glwe_size: GlweSize,
poly_size: PolynomialSize,
) -> usize {
// One ciphertext per level encrypted under the output key
decomp_level_count.0 * output_glwe_size.0 * poly_size.0
}
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> GlweKeyswitchKey<C> {
/// Create an [`GlweKeyswitchKey`] from an existing container.
///
/// # Note
///
/// This function only wraps a container in the appropriate type. If you want to generate an
/// [`GlweKeyswitchKey`] you need to call
/// [`crate::core_crypto::algorithms::generate_glwe_keyswitch_key`] using this key as output.
///
/// This docstring exhibits [`GlweKeyswitchKey`] primitives usage.
///
/// ```
/// use tfhe::core_crypto::prelude::*;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for LweKeyswitchKey creation
/// let input_glwe_dimension = GlweDimension(1);
/// let output_glwe_dimension = GlweDimension(2);
/// let poly_size = PolynomialSize(1024);
/// let decomp_base_log = DecompositionBaseLog(4);
/// let decomp_level_count = DecompositionLevelCount(5);
/// let ciphertext_modulus = CiphertextModulus::new_native();
///
/// // Create a new LweKeyswitchKey
/// let glwe_ksk = GlweKeyswitchKey::new(
/// 0u64,
/// decomp_base_log,
/// decomp_level_count,
/// input_glwe_dimension,
/// output_glwe_dimension,
/// poly_size,
/// ciphertext_modulus,
/// );
///
/// assert_eq!(glwe_ksk.decomposition_base_log(), decomp_base_log);
/// assert_eq!(glwe_ksk.decomposition_level_count(), decomp_level_count);
/// assert_eq!(glwe_ksk.input_key_glwe_dimension(), input_glwe_dimension);
/// assert_eq!(glwe_ksk.output_key_glwe_dimension(), output_glwe_dimension);
/// assert_eq!(glwe_ksk.polynomial_size(), poly_size);
/// assert_eq!(
/// glwe_ksk.output_glwe_size(),
/// output_glwe_dimension.to_glwe_size()
/// );
/// assert_eq!(glwe_ksk.ciphertext_modulus(), ciphertext_modulus);
///
/// // Demonstrate how to recover the allocated container
/// let underlying_container: Vec<u64> = glwe_ksk.into_container();
///
/// // Recreate a keyswithc key using from_container
/// let glwe_ksk = GlweKeyswitchKey::from_container(
/// underlying_container,
/// decomp_base_log,
/// decomp_level_count,
/// output_glwe_dimension.to_glwe_size(),
/// poly_size,
/// ciphertext_modulus,
/// );
///
/// assert_eq!(glwe_ksk.decomposition_base_log(), decomp_base_log);
/// assert_eq!(glwe_ksk.decomposition_level_count(), decomp_level_count);
/// assert_eq!(glwe_ksk.input_key_glwe_dimension(), input_glwe_dimension);
/// assert_eq!(glwe_ksk.output_key_glwe_dimension(), output_glwe_dimension);
/// assert_eq!(
/// glwe_ksk.output_glwe_size(),
/// output_glwe_dimension.to_glwe_size()
/// );
/// assert_eq!(glwe_ksk.ciphertext_modulus(), ciphertext_modulus);
/// ```
pub fn from_container(
container: C,
decomp_base_log: DecompositionBaseLog,
decomp_level_count: DecompositionLevelCount,
output_glwe_size: GlweSize,
poly_size: PolynomialSize,
ciphertext_modulus: CiphertextModulus<C::Element>,
) -> Self {
assert!(
container.container_len() > 0,
"Got an empty container to create an LweKeyswitchKey"
);
assert!(
container.container_len() % (decomp_level_count.0 * output_glwe_size.0 * poly_size.0)
== 0,
"The provided container length is not valid. \
It needs to be dividable by decomp_level_count * output_glwe_size * output_poly_size: {}. \
Got container length: {} and decomp_level_count: {decomp_level_count:?}, \
output_glwe_size: {output_glwe_size:?}, poly_size: {poly_size:?}.",
decomp_level_count.0 * output_glwe_size.0 * poly_size.0,
container.container_len()
);
GlweKeyswitchKey {
data: container,
decomp_base_log,
decomp_level_count,
output_glwe_size,
poly_size,
ciphertext_modulus,
}
}
/// Return the [`DecompositionBaseLog`] of the [`LweKeyswitchKey`].
///
/// See [`LweKeyswitchKey::from_container`] for usage.
pub fn decomposition_base_log(&self) -> DecompositionBaseLog {
self.decomp_base_log
}
/// Return the [`DecompositionLevelCount`] of the [`LweKeyswitchKey`].
///
/// See [`LweKeyswitchKey::from_container`] for usage.
pub fn decomposition_level_count(&self) -> DecompositionLevelCount {
self.decomp_level_count
}
/// Return the input [`GlweDimension`] of the [`GlweKeyswitchKey`].
///
/// See [`GlweKeyswitchKey::from_container`] for usage.
pub fn input_key_glwe_dimension(&self) -> GlweDimension {
GlweDimension(self.data.container_len() / self.input_key_element_encrypted_size())
}
/// Return the input [`PolynomialSize`] of the [`GlweKeyswitchKey`].
///
/// See [`GlweKeyswitchKey::from_container`] for usage.
pub fn polynomial_size(&self) -> PolynomialSize {
self.poly_size
}
/// Return the output [`GlweDimension`] of the [`GlweKeyswitchKey`].
///
/// See [`GlweKeyswitchKey::from_container`] for usage.
pub fn output_key_glwe_dimension(&self) -> GlweDimension {
self.output_glwe_size.to_glwe_dimension()
}
/// Return the output [`GlweSize`] of the [`GlweKeyswitchKey`].
///
/// See [`GlweKeyswitchKey::from_container`] for usage.
pub fn output_glwe_size(&self) -> GlweSize {
self.output_glwe_size
}
/// Return the number of elements in an encryption of an input [`GlweSecretKey`] element of the
/// current [`GlweKeyswitchKey`].
pub fn input_key_element_encrypted_size(&self) -> usize {
glwe_keyswitch_key_input_key_element_encrypted_size(
self.decomp_level_count,
self.output_glwe_size,
self.poly_size,
)
}
/// Return a view of the [`GlweKeyswitchKey`]. This is useful if an algorithm takes a view by
/// value.
pub fn as_view(&self) -> GlweKeyswitchKey<&'_ [Scalar]> {
GlweKeyswitchKey::from_container(
self.as_ref(),
self.decomp_base_log,
self.decomp_level_count,
self.output_glwe_size,
self.poly_size,
self.ciphertext_modulus,
)
}
/// Consume the entity and return its underlying container.
///
/// See [`GlweKeyswitchKey::from_container`] for usage.
pub fn into_container(self) -> C {
self.data
}
/// Return the [`CiphertextModulus`] of the [`GlweKeyswitchKey`].
///
/// See [`GlweKeyswitchKey::from_container`] for usage.
pub fn ciphertext_modulus(&self) -> CiphertextModulus<C::Element> {
self.ciphertext_modulus
}
pub fn as_glwe_ciphertext_list(&self) -> GlweCiphertextListView<'_, Scalar> {
GlweCiphertextListView::from_container(
self.as_ref(),
self.output_glwe_size(),
self.polynomial_size(),
self.ciphertext_modulus(),
)
}
}
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> GlweKeyswitchKey<C> {
/// Mutable variant of [`GlweKeyswitchKey::as_view`].
pub fn as_mut_view(&mut self) -> GlweKeyswitchKey<&'_ mut [Scalar]> {
let decomp_base_log = self.decomp_base_log;
let decomp_level_count = self.decomp_level_count;
let output_glwe_size = self.output_glwe_size;
let poly_size = self.poly_size;
let ciphertext_modulus = self.ciphertext_modulus;
GlweKeyswitchKey::from_container(
self.as_mut(),
decomp_base_log,
decomp_level_count,
output_glwe_size,
poly_size,
ciphertext_modulus,
)
}
pub fn as_mut_glwe_ciphertext_list(&mut self) -> GlweCiphertextListMutView<'_, Scalar> {
let output_glwe_size = self.output_glwe_size();
let poly_size = self.polynomial_size();
let ciphertext_modulus = self.ciphertext_modulus();
GlweCiphertextListMutView::from_container(
self.as_mut(),
output_glwe_size,
poly_size,
ciphertext_modulus,
)
}
}
/// A [`GlweKeyswitchKey`] owning the memory for its own storage.
pub type GlweKeyswitchKeyOwned<Scalar> = GlweKeyswitchKey<Vec<Scalar>>;
impl<Scalar: UnsignedInteger> GlweKeyswitchKeyOwned<Scalar> {
/// Allocate memory and create a new owned [`GlweKeyswitchKey`].
///
/// # Note
///
/// This function allocates a vector of the appropriate size and wraps it in the appropriate
/// type. If you want to generate an [`GlweKeyswitchKey`] you need to call
/// [`crate::core_crypto::algorithms::generate_glwe_keyswitch_key`] using this key as output.
///
/// See [`GlweKeyswitchKey::from_container`] for usage.
pub fn new(
fill_with: Scalar,
decomp_base_log: DecompositionBaseLog,
decomp_level_count: DecompositionLevelCount,
input_key_glwe_dimension: GlweDimension,
output_key_glwe_dimension: GlweDimension,
poly_size: PolynomialSize,
ciphertext_modulus: CiphertextModulus<Scalar>,
) -> GlweKeyswitchKeyOwned<Scalar> {
GlweKeyswitchKeyOwned::from_container(
vec![
fill_with;
input_key_glwe_dimension.0
* glwe_keyswitch_key_input_key_element_encrypted_size(
decomp_level_count,
output_key_glwe_dimension.to_glwe_size(),
poly_size,
)
],
decomp_base_log,
decomp_level_count,
output_key_glwe_dimension.to_glwe_size(),
poly_size,
ciphertext_modulus,
)
}
}
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> ContiguousEntityContainer
for GlweKeyswitchKey<C>
{
type Element = C::Element;
type EntityViewMetadata = GlweCiphertextListCreationMetadata<Scalar>;
type EntityView<'this> = GlweCiphertextListView<'this, Self::Element>
where
Self: 'this;
type SelfViewMetadata = ();
// At the moment it does not make sense to return "sub" keyswitch keys. So we use a dummy
// placeholder type here.
type SelfView<'this> = DummyCreateFrom
where
Self: 'this;
fn get_entity_view_creation_metadata(&self) -> GlweCiphertextListCreationMetadata<Scalar> {
GlweCiphertextListCreationMetadata(
self.output_glwe_size(),
self.polynomial_size(),
self.ciphertext_modulus(),
)
}
fn get_entity_view_pod_size(&self) -> usize {
self.input_key_element_encrypted_size()
}
/// Unimplemented for [`GlweKeyswitchKey`]. At the moment it does not make sense to
/// return "sub" keyswitch keys.
fn get_self_view_creation_metadata(&self) {
unimplemented!(
"This function is not supported for GlweKeyswitchKey. \
At the moment it does not make sense to return 'sub' keyswitch keys."
)
}
}
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> ContiguousEntityContainerMut
for GlweKeyswitchKey<C>
{
type EntityMutView<'this> = GlweCiphertextListMutView<'this, Self::Element>
where
Self: 'this;
// At the moment it does not make sense to return "sub" keyswitch keys. So we use a dummy
// placeholder type here.
type SelfMutView<'this> = DummyCreateFrom
where
Self: 'this;
}

View File

@@ -0,0 +1,382 @@
//! Module containing the definition of the GlweRelinearisationKey.
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
/// A [`GLWE relinearisation key`](`GlweRelinearisationKey`).
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct GlweRelinearisationKey<C: Container>
where
C::Element: UnsignedInteger,
{
data: C,
decomp_base_log: DecompositionBaseLog,
decomp_level_count: DecompositionLevelCount,
glwe_size: GlweSize,
polynomial_size: PolynomialSize,
ciphertext_modulus: CiphertextModulus<C::Element>,
}
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]> for GlweRelinearisationKey<C> {
fn as_ref(&self) -> &[T] {
self.data.as_ref()
}
}
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]> for GlweRelinearisationKey<C> {
fn as_mut(&mut self) -> &mut [T] {
self.data.as_mut()
}
}
/// Return the number of elements in an encryption of an input [`GlweSecretKey`] element for a
/// [`GlweRelinearisationKey`] given a [`DecompositionLevelCount`], [`GlweSize`] and
/// [`PolynomialSize`].
pub fn glwe_relinearisation_key_input_key_element_encrypted_size(
decomp_level_count: DecompositionLevelCount,
glwe_size: GlweSize,
polynomial_size: PolynomialSize,
) -> usize {
// One ciphertext per level encrypted under the output key
decomp_level_count.0 * glwe_size.0 * polynomial_size.0
}
/// Return the number of elements in a [`GlweRelinearisationKey`] given a
/// [`DecompositionLevelCount`], [`GlweSize`], and [`PolynomialSize`].
pub fn glwe_relinearisation_key_size(
decomp_level_count: DecompositionLevelCount,
glwe_size: GlweSize,
polynomial_size: PolynomialSize,
) -> usize {
(glwe_size.to_glwe_dimension().0 * (glwe_size.to_glwe_dimension().0 + 1)) / 2
* glwe_relinearisation_key_input_key_element_encrypted_size(
decomp_level_count,
glwe_size,
polynomial_size,
)
}
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> GlweRelinearisationKey<C> {
/// Create a [`GlweRelinearisationKey`] from an existing container.
///
/// # Note
///
/// This function only wraps a container in the appropriate type. If you want to generate an
/// [`GlweRelinearisationKey`] you need to use
/// [`crate::core_crypto::algorithms::generate_glwe_relinearisation_key`]
/// using this key as output.
///
/// This docstring exhibits [`GlweRelinearisationKey`] primitives usage.
///
/// ```
/// use tfhe::core_crypto::prelude::*;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for GlweRelinearisationKey creation
/// let glwe_size = GlweSize(3);
/// let polynomial_size = PolynomialSize(1024);
/// let decomp_base_log = DecompositionBaseLog(8);
/// let decomp_level_count = DecompositionLevelCount(3);
/// let ciphertext_modulus = CiphertextModulus::new_native();
///
/// // Create a new GlweRelinearisationKey
/// let relin_key = GlweRelinearisationKey::new(
/// 0u64,
/// decomp_base_log,
/// decomp_level_count,
/// glwe_size,
/// polynomial_size,
/// ciphertext_modulus,
/// );
///
/// assert_eq!(relin_key.glwe_dimension(), glwe_size.to_glwe_dimension());
/// assert_eq!(relin_key.glwe_size(), glwe_size);
/// assert_eq!(relin_key.polynomial_size(), polynomial_size);
/// assert_eq!(relin_key.decomposition_base_log(), decomp_base_log);
/// assert_eq!(relin_key.decomposition_level_count(), decomp_level_count);
/// assert_eq!(relin_key.ciphertext_modulus(), ciphertext_modulus);
///
/// // Demonstrate how to recover the allocated container
/// let underlying_container: Vec<u64> = relin_key.into_container();
///
/// // Recreate a key using from_container
/// let relin_key = GlweRelinearisationKey::from_container(
/// underlying_container,
/// decomp_base_log,
/// decomp_level_count,
/// glwe_size,
/// polynomial_size,
/// ciphertext_modulus,
/// );
///
/// assert_eq!(relin_key.glwe_dimension(), glwe_size.to_glwe_dimension());
/// assert_eq!(relin_key.glwe_size(), glwe_size);
/// assert_eq!(relin_key.polynomial_size(), polynomial_size);
/// assert_eq!(relin_key.decomposition_base_log(), decomp_base_log);
/// assert_eq!(relin_key.decomposition_level_count(), decomp_level_count);
/// assert_eq!(relin_key.ciphertext_modulus(), ciphertext_modulus);
/// ```
pub fn from_container(
container: C,
decomp_base_log: DecompositionBaseLog,
decomp_level_count: DecompositionLevelCount,
glwe_size: GlweSize,
polynomial_size: PolynomialSize,
ciphertext_modulus: CiphertextModulus<C::Element>,
) -> GlweRelinearisationKey<C> {
assert!(
container.container_len() > 0,
"Got an empty container to create an LweKeyswitchKey"
);
assert!(
container.container_len()
% glwe_relinearisation_key_input_key_element_encrypted_size(
decomp_level_count,
glwe_size,
polynomial_size
)
== 0,
"The provided container length is not valid. \
It needs to be divisable by decomp_level_count * glwe_size * polynomial_size:\
{}. Got container length: {} and decomp_level_count: {decomp_level_count:?}, \
glwe_size: {glwe_size:?}, polynomial_size: {polynomial_size:?}.",
glwe_relinearisation_key_input_key_element_encrypted_size(
decomp_level_count,
glwe_size,
polynomial_size
),
container.container_len()
);
GlweRelinearisationKey {
data: container,
decomp_base_log,
decomp_level_count,
glwe_size,
polynomial_size,
ciphertext_modulus,
}
}
/// Return the [`GlweDimension`] of the [`GlweRelinearisationKey`].
///
/// See [`GlweRelinearisationKey::from_container`] for usage.
pub fn glwe_dimension(&self) -> GlweDimension {
self.glwe_size.to_glwe_dimension()
}
/// Return the [`GlweSize`] of the [`GlweRelinearisationKey`].
///
/// See [`GlweRelinearisationKey::from_container`] for usage.
pub fn glwe_size(&self) -> GlweSize {
self.glwe_size
}
/// Return the output [`PolynomialSize`] of the [`GlweRelinearisationKey`].
///
/// See [`GlweRelinearisationKey::from_container`] for usage.
pub fn polynomial_size(&self) -> PolynomialSize {
self.polynomial_size
}
/// Return the [`DecompositionLevelCount`] of the [`GlweRelinearisationKey`].
///
/// See [`GlweRelinearisationKey::from_container`] for usage.
pub fn decomposition_level_count(&self) -> DecompositionLevelCount {
self.decomp_level_count
}
/// Return the [`DecompositionBaseLog`] of the [`GlweRelinearisationKey`].
///
/// See [`GlweRelinearisationKey::from_container`] for usage.
pub fn decomposition_base_log(&self) -> DecompositionBaseLog {
self.decomp_base_log
}
/// Return the number of elements in an encryption of an input [`GlweSecretKey`] element of the
/// current [`GlweRelinearisationKey`].
pub fn input_key_element_encrypted_size(&self) -> usize {
glwe_relinearisation_key_input_key_element_encrypted_size(
self.decomp_level_count,
self.glwe_size,
self.polynomial_size,
)
}
/// Return a view of the [`GlweRelinearisationKey`]. This is useful if an
/// algorithm takes a view by value.
pub fn as_view(&self) -> GlweRelinearisationKey<&'_ [Scalar]> {
GlweRelinearisationKey::from_container(
self.as_ref(),
self.decomp_base_log,
self.decomp_level_count,
self.glwe_size,
self.polynomial_size,
self.ciphertext_modulus,
)
}
/// Consume the entity and return its underlying container.
///
/// See [`GlweRelinearisationKey::from_container`] for usage.
pub fn into_container(self) -> C {
self.data
}
/// Return the [`CiphertextModulus`] of the [`GlweRelinearisationKey`]
///
/// See [`GlweRelinearisationKey::from_container`] for usage.
pub fn ciphertext_modulus(&self) -> CiphertextModulus<C::Element> {
self.ciphertext_modulus
}
}
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> GlweRelinearisationKey<C> {
/// Mutable variant of [`LweTracePackingKeyswitchKey::as_view`].
pub fn as_mut_view(&mut self) -> GlweRelinearisationKey<&'_ mut [Scalar]> {
let decomp_base_log = self.decomp_base_log;
let decomp_level_count = self.decomp_level_count;
let glwe_size = self.glwe_size;
let polynomial_size = self.polynomial_size;
let ciphertext_modulus = self.ciphertext_modulus;
GlweRelinearisationKey::from_container(
self.as_mut(),
decomp_base_log,
decomp_level_count,
glwe_size,
polynomial_size,
ciphertext_modulus,
)
}
}
/// A [`GlweRelinearisationKey`] owning the memory for its own storage.
pub type GlweRelinearisationKeyOwned<Scalar> = GlweRelinearisationKey<Vec<Scalar>>;
impl<Scalar: UnsignedInteger> GlweRelinearisationKeyOwned<Scalar> {
/// Create a new [`GlweRelinearisationKey`].
///
/// # Note
///
/// This function allocates a vector of the appropriate size and wraps it in the appropriate
/// type. If you want to generate an [`GlweRelinearisationKey`] you need to use
/// [`crate::core_crypto::algorithms::generate_glwe_relinearisation_key`]
/// using this key as output.
///
/// See [`GlweRelinearisationKey::from_container`] for usage.
pub fn new(
fill_with: Scalar,
decomp_base_log: DecompositionBaseLog,
decomp_level_count: DecompositionLevelCount,
glwe_size: GlweSize,
polynomial_size: PolynomialSize,
ciphertext_modulus: CiphertextModulus<Scalar>,
) -> GlweRelinearisationKeyOwned<Scalar> {
GlweRelinearisationKeyOwned::from_container(
vec![
fill_with;
glwe_relinearisation_key_size(decomp_level_count, glwe_size, polynomial_size)
],
decomp_base_log,
decomp_level_count,
glwe_size,
polynomial_size,
ciphertext_modulus,
)
}
}
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> ContiguousEntityContainer
for GlweRelinearisationKey<C>
{
type Element = C::Element;
type EntityViewMetadata = GlweCiphertextListCreationMetadata<Scalar>;
type EntityView<'this> = GlweCiphertextListView<'this, Self::Element>
where
Self: 'this;
type SelfViewMetadata = ();
// At the moment it does not make sense to return "sub" packing keyswitch keys. So we use a
// dummy placeholder type here.
type SelfView<'this> = DummyCreateFrom
where
Self: 'this;
fn get_entity_view_creation_metadata(&self) -> Self::EntityViewMetadata {
// Fix to use the correct ciphertext modulus
GlweCiphertextListCreationMetadata(
self.glwe_size,
self.polynomial_size,
self.ciphertext_modulus(),
)
}
fn get_entity_view_pod_size(&self) -> usize {
self.input_key_element_encrypted_size()
}
/// Unimplemented for [`GlweRelinearisationKey`]. At the moment it does not
/// make sense to return "sub" packing keyswitch keys.
fn get_self_view_creation_metadata(&self) -> Self::SelfViewMetadata {
unimplemented!(
"This function is not supported for GlweRelinearisationKey. \
At the moment it does not make sense to return 'sub' relinearisation keys."
)
}
}
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> ContiguousEntityContainerMut
for GlweRelinearisationKey<C>
{
type EntityMutView<'this> = GlweCiphertextListMutView<'this, Self::Element>
where
Self: 'this;
// At the moment it does not make sense to return "sub" relinearisation keys. So we use a
// dummy placeholder type here.
type SelfMutView<'this> = DummyCreateFrom
where
Self: 'this;
}
/// Metadata used in the [`CreateFrom`] implementation to create
/// [`GlweRelinearisationKey`] entities.
#[derive(Clone, Copy)]
pub struct GlweRelinearisationKeyCreationMetadata<Scalar: UnsignedInteger>(
pub DecompositionBaseLog,
pub DecompositionLevelCount,
pub GlweSize,
pub PolynomialSize,
pub CiphertextModulus<Scalar>,
);
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CreateFrom<C>
for GlweRelinearisationKey<C>
{
type Metadata = GlweRelinearisationKeyCreationMetadata<Scalar>;
#[inline]
fn create_from(from: C, meta: Self::Metadata) -> GlweRelinearisationKey<C> {
let GlweRelinearisationKeyCreationMetadata(
decomp_base_log,
decomp_level_count,
glwe_size,
polynomial_size,
ciphertext_modulus,
) = meta;
GlweRelinearisationKey::from_container(
from,
decomp_base_log,
decomp_level_count,
glwe_size,
polynomial_size,
ciphertext_modulus,
)
}
}

View File

@@ -0,0 +1,421 @@
//! Module containing the definition of the LwePublicFunctionalPackingKeyswitchKey.
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
/// A [`LWE public functional packing keyswitch key`](`LwePublicFunctionalPackingKeyswitchKey`).
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct LwePublicFunctionalPackingKeyswitchKey<C: Container>
where
C::Element: UnsignedInteger,
{
data: C,
decomp_base_log: DecompositionBaseLog,
decomp_level_count: DecompositionLevelCount,
output_glwe_size: GlweSize,
output_polynomial_size: PolynomialSize,
ciphertext_modulus: CiphertextModulus<C::Element>,
}
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]>
for LwePublicFunctionalPackingKeyswitchKey<C>
{
fn as_ref(&self) -> &[T] {
self.data.as_ref()
}
}
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]>
for LwePublicFunctionalPackingKeyswitchKey<C>
{
fn as_mut(&mut self) -> &mut [T] {
self.data.as_mut()
}
}
/// Return the number of elements in an encryption of a list of input [`LweSecretKey`] elements for
/// a [`LwePublicFunctionalPackingKeyswitchKey`] given a [`DecompositionLevelCount`] and output
/// [`GlweSize`] and [`PolynomialSize`].
pub fn lwe_pubfpksk_input_key_element_encrypted_size(
decomp_level_count: DecompositionLevelCount,
output_glwe_size: GlweSize,
output_polynomial_size: PolynomialSize,
) -> usize {
// One ciphertext per level encrypted under the output key
decomp_level_count.0 * output_glwe_size.0 * output_polynomial_size.0
}
/// Return the number of elements in an [`LwePublicFunctionalPackingKeyswitchKey`] given an input
/// ['size of the list of LWe] [`LweSize`], [`DecompositionLevelCount`], output [`GlweSize`], and
/// output [`PolynomialSize`].
pub fn lwe_pubfpksk_size(
input_lwe_size: LweSize,
decomp_level_count: DecompositionLevelCount,
output_glwe_size: GlweSize,
output_polynomial_size: PolynomialSize,
) -> usize {
input_lwe_size.0
* lwe_pubfpksk_input_key_element_encrypted_size(
decomp_level_count,
output_glwe_size,
output_polynomial_size,
)
}
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>>
LwePublicFunctionalPackingKeyswitchKey<C>
{
/// Create an [`LwePublicFunctionalPackingKeyswitchKey`] from an existing container.
///
/// # Note
///
/// This function only wraps a container in the appropriate type. If you want to generate an
/// [`LwePublicFunctionalPackingKeyswitchKey`] you need to use
/// [`crate::core_crypto::algorithms::generate_lwe_public_functional_packing_keyswitch_key`] or
/// the parallel variant
/// [`crate::core_crypto::algorithms::par_generate_lwe_public_functional_packing_keyswitch_key`]
/// using this key as output.
///
/// This docstring exhibits [`LwePublicFunctionalPackingKeyswitchKey`] primitives usage.
///
/// ```
/// use tfhe::core_crypto::prelude::*;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for LwePublicFunctionalPackingKeyswitchKey creation
/// let glwe_size = GlweSize(2);
/// let polynomial_size = PolynomialSize(1024);
/// let decomp_base_log = DecompositionBaseLog(8);
/// let decomp_level_count = DecompositionLevelCount(3);
/// let input_lwe_dimension = LweDimension(600);
/// let ciphertext_modulus = CiphertextModulus::new_native();
///
/// // Create a new LwePublicFunctionalPackingKeyswitchKey
/// let pubfpksk = LwePublicFunctionalPackingKeyswitchKey::new(
/// 0u64,
/// decomp_base_log,
/// decomp_level_count,
/// input_lwe_dimension,
/// glwe_size,
/// polynomial_size,
/// ciphertext_modulus,
/// );
///
/// assert_eq!(
/// pubfpksk.output_glwe_key_dimension(),
/// glwe_size.to_glwe_dimension()
/// );
/// assert_eq!(pubfpksk.output_glwe_size(), glwe_size);
/// assert_eq!(pubfpksk.output_polynomial_size(), polynomial_size);
/// assert_eq!(pubfpksk.decomposition_base_log(), decomp_base_log);
/// assert_eq!(pubfpksk.decomposition_level_count(), decomp_level_count);
/// assert_eq!(pubfpksk.input_lwe_key_dimension(), input_lwe_dimension);
/// assert_eq!(pubfpksk.ciphertext_modulus(), ciphertext_modulus);
///
/// // Demonstrate how to recover the allocated container
/// let underlying_container: Vec<u64> = pubfpksk.into_container();
///
/// // Recreate a key using from_container
/// let pubfpksk = LwePublicFunctionalPackingKeyswitchKeyList::from_container(
/// underlying_container,
/// decomp_base_log,
/// decomp_level_count,
/// input_lwe_dimension.to_lwe_size(),
/// glwe_size,
/// polynomial_size,
/// ciphertext_modulus,
/// );
///
/// assert_eq!(
/// pubfpksk.output_glwe_key_dimension(),
/// glwe_size.to_glwe_dimension()
/// );
/// assert_eq!(pubfpksk.output_glwe_size(), glwe_size);
/// assert_eq!(pubfpksk.output_polynomial_size(), polynomial_size);
/// assert_eq!(pubfpksk.decomposition_base_log(), decomp_base_log);
/// assert_eq!(pubfpksk.decomposition_level_count(), decomp_level_count);
/// assert_eq!(pubfpksk.input_lwe_key_dimension(), input_lwe_dimension);
/// assert_eq!(pubfpksk.ciphertext_modulus(), ciphertext_modulus);
/// ```
pub fn from_container(
container: C,
decomp_base_log: DecompositionBaseLog,
decomp_level_count: DecompositionLevelCount,
output_glwe_size: GlweSize,
output_polynomial_size: PolynomialSize,
ciphertext_modulus: CiphertextModulus<C::Element>,
) -> LwePublicFunctionalPackingKeyswitchKey<C> {
assert!(
container.container_len() > 0,
"Got an empty container to create an LweKeyswitchKey"
);
assert!(
container.container_len()
% lwe_pubfpksk_input_key_element_encrypted_size(
decomp_level_count,
output_glwe_size,
output_polynomial_size
)
== 0,
"The provided container length is not valid. \
It needs to be dividable by decomp_level_count * output_glwe_size * output_polynomial_size:\
{}. Got container length: {} and decomp_level_count: {decomp_level_count:?}, \
output_glwe_size: {output_glwe_size:?}, output_polynomial_size: \
{output_polynomial_size:?}.",
lwe_pubfpksk_input_key_element_encrypted_size(
decomp_level_count,
output_glwe_size,
output_polynomial_size
),
container.container_len()
);
LwePublicFunctionalPackingKeyswitchKey {
data: container,
decomp_base_log,
decomp_level_count,
output_glwe_size,
output_polynomial_size,
ciphertext_modulus,
}
}
/// Return the output key [`GlweDimension`] of the [`LwePublicFunctionalPackingKeyswitchKey`].
///
/// See [`LwePublicFunctionalPackingKeyswitchKey::from_container`] for usage.
pub fn output_glwe_key_dimension(&self) -> GlweDimension {
self.output_glwe_size.to_glwe_dimension()
}
/// Return the output [`GlweSize`] of the [`LwePublicFunctionalPackingKeyswitchKey`].
///
/// See [`LwePublicFunctionalPackingKeyswitchKey::from_container`] for usage.
pub fn output_glwe_size(&self) -> GlweSize {
self.output_glwe_size
}
/// Return the output [`PolynomialSize`] of the [`LwePublicFunctionalPackingKeyswitchKey`].
///
/// See [`LwePublicFunctionalPackingKeyswitchKey::from_container`] for usage.
pub fn output_polynomial_size(&self) -> PolynomialSize {
self.output_polynomial_size
}
/// Return the input key [`LweDimension`] of the [`LwePublicFunctionalPackingKeyswitchKey`].
///
/// See [`LwePublicFunctionalPackingKeyswitchKey::from_container`] for usage.
pub fn input_lwe_key_dimension(&self) -> LweDimension {
LweDimension(self.data.container_len() / self.input_key_element_encrypted_size() - 1)
}
/// Return the [`DecompositionLevelCount`] of the [`LwePublicFunctionalPackingKeyswitchKey`].
///
/// See [`LwePublicFunctionalPackingKeyswitchKey::from_container`] for usage.
pub fn decomposition_level_count(&self) -> DecompositionLevelCount {
self.decomp_level_count
}
/// Return the [`DecompositionBaseLog`] of the [`LwePublicFunctionalPackingKeyswitchKey`].
///
/// See [`LwePublicFunctionalPackingKeyswitchKey::from_container`] for usage.
pub fn decomposition_base_log(&self) -> DecompositionBaseLog {
self.decomp_base_log
}
/// Return the number of elements in an encryption of an input [`LweSecretKey`] element of the
/// current [`LwePublicFunctionalPackingKeyswitchKey`].
pub fn input_key_element_encrypted_size(&self) -> usize {
lwe_pubfpksk_input_key_element_encrypted_size(
self.decomp_level_count,
self.output_glwe_size,
self.output_polynomial_size,
)
}
/// Return a view of the [`LwePublicFunctionalPackingKeyswitchKey`]. This is useful if an
/// algorithm takes a view by value.
pub fn as_view(&self) -> LwePublicFunctionalPackingKeyswitchKey<&'_ [Scalar]> {
LwePublicFunctionalPackingKeyswitchKey::from_container(
self.as_ref(),
self.decomp_base_log,
self.decomp_level_count,
self.output_glwe_size,
self.output_polynomial_size,
self.ciphertext_modulus,
)
}
/// Consume the entity and return its underlying container.
///
/// See [`LwePublicFunctionalPackingKeyswitchKey::from_container`] for usage.
pub fn into_container(self) -> C {
self.data
}
/// Return the [`CiphertextModulus`] of the [`LwePublicFunctionalPackingKeyswitchKey`]
///
/// See [`LwePublicFunctionalPackingKeyswitchKey::from_container`] for usage.
pub fn ciphertext_modulus(&self) -> CiphertextModulus<C::Element> {
self.ciphertext_modulus
}
}
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>>
LwePublicFunctionalPackingKeyswitchKey<C>
{
/// Mutable variant of [`LwePublicFunctionalPackingKeyswitchKey::as_view`].
pub fn as_mut_view(&mut self) -> LwePublicFunctionalPackingKeyswitchKey<&'_ mut [Scalar]> {
let decomp_base_log = self.decomp_base_log;
let decomp_level_count = self.decomp_level_count;
let output_glwe_size = self.output_glwe_size;
let output_polynomial_size = self.output_polynomial_size;
let ciphertext_modulus = self.ciphertext_modulus;
LwePublicFunctionalPackingKeyswitchKey::from_container(
self.as_mut(),
decomp_base_log,
decomp_level_count,
output_glwe_size,
output_polynomial_size,
ciphertext_modulus,
)
}
}
/// An [`LwePublicFunctionalPackingKeyswitchKey`] owning the memory for its own storage.
pub type LwePublicFunctionalPackingKeyswitchKeyOwned<Scalar> =
LwePublicFunctionalPackingKeyswitchKey<Vec<Scalar>>;
impl<Scalar: UnsignedInteger> LwePublicFunctionalPackingKeyswitchKeyOwned<Scalar> {
/// Create an [`LwePublicFunctionalPackingKeyswitchKey`] from an existing container.
///
/// # Note
///
/// This function allocates a vector of the appropriate size and wraps it in the appropriate
/// type. If you want to generate an [`LwePublicFunctionalPackingKeyswitchKey`] you need to use
/// [`crate::core_crypto::algorithms::generate_lwe_public_functional_packing_keyswitch_key`] or
/// the parallel variant
/// [`crate::core_crypto::algorithms::par_generate_lwe_public_functional_packing_keyswitch_key`]
/// using this key as output.
///
/// See [`LwePublicFunctionalPackingKeyswitchKey::from_container`] for usage.
pub fn new(
fill_with: Scalar,
decomp_base_log: DecompositionBaseLog,
decomp_level_count: DecompositionLevelCount,
input_key_lwe_dimension: LweDimension,
output_glwe_size: GlweSize,
output_polynomial_size: PolynomialSize,
ciphertext_modulus: CiphertextModulus<Scalar>,
) -> LwePublicFunctionalPackingKeyswitchKeyOwned<Scalar> {
LwePublicFunctionalPackingKeyswitchKeyOwned::from_container(
vec![
fill_with;
lwe_pubfpksk_size(
input_key_lwe_dimension.to_lwe_size(),
decomp_level_count,
output_glwe_size,
output_polynomial_size
)
],
decomp_base_log,
decomp_level_count,
output_glwe_size,
output_polynomial_size,
ciphertext_modulus,
)
}
}
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> ContiguousEntityContainer
for LwePublicFunctionalPackingKeyswitchKey<C>
{
type Element = C::Element;
type EntityViewMetadata = GlweCiphertextListCreationMetadata<Scalar>;
type EntityView<'this> = GlweCiphertextListView<'this, Self::Element>
where
Self: 'this;
type SelfViewMetadata = ();
// At the moment it does not make sense to return "sub" packing keyswitch keys. So we use a
// dummy placeholder type here.
type SelfView<'this> = DummyCreateFrom
where
Self: 'this;
fn get_entity_view_creation_metadata(&self) -> Self::EntityViewMetadata {
GlweCiphertextListCreationMetadata(
self.output_glwe_size,
self.output_polynomial_size,
self.ciphertext_modulus,
)
}
fn get_entity_view_pod_size(&self) -> usize {
self.input_key_element_encrypted_size()
}
/// Unimplemented for [`LwePublicFunctionalPackingKeyswitchKey`]. At the moment it does not
/// make sense to return "sub" packing keyswitch keys.
fn get_self_view_creation_metadata(&self) -> Self::SelfViewMetadata {
unimplemented!(
"This function is not supported for LwePublicFunctionalPackingKeyswitchKey. \
At the moment it does not make sense to return 'sub' packing keyswitch keys."
)
}
}
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> ContiguousEntityContainerMut
for LwePublicFunctionalPackingKeyswitchKey<C>
{
type EntityMutView<'this> = GlweCiphertextListMutView<'this, Self::Element>
where
Self: 'this;
// At the moment it does not make sense to return "sub" packing keyswitch keys. So we use a
// dummy placeholder type here.
type SelfMutView<'this> = DummyCreateFrom
where
Self: 'this;
}
/// Metadata used in the [`CreateFrom`] implementation to create
/// [`LwePublicFunctionalPackingKeyswitchKey`] entities.
#[derive(Clone, Copy)]
pub struct LwePublicFunctionalPackingKeyswitchKeyCreationMetadata<Scalar: UnsignedInteger>(
pub DecompositionBaseLog,
pub DecompositionLevelCount,
pub GlweSize,
pub PolynomialSize,
pub CiphertextModulus<Scalar>,
);
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CreateFrom<C>
for LwePublicFunctionalPackingKeyswitchKey<C>
{
type Metadata = LwePublicFunctionalPackingKeyswitchKeyCreationMetadata<Scalar>;
#[inline]
fn create_from(from: C, meta: Self::Metadata) -> LwePublicFunctionalPackingKeyswitchKey<C> {
let LwePublicFunctionalPackingKeyswitchKeyCreationMetadata(
decomp_base_log,
decomp_level_count,
output_glwe_size,
output_polynomial_size,
ciphertext_modulus,
) = meta;
LwePublicFunctionalPackingKeyswitchKey::from_container(
from,
decomp_base_log,
decomp_level_count,
output_glwe_size,
output_polynomial_size,
ciphertext_modulus,
)
}
}

View File

@@ -0,0 +1,444 @@
//! Module containing the definition of the LwePublicFunctionalPackingKeyswitchKeyList.
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
/// A contiguous list containing [`LWE private functional packing keyswitch
/// keys`](`crate::core_crypto::entities::LwePrivateFunctionalPackingKeyswitchKey`).
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct LwePublicFunctionalPackingKeyswitchKeyList<C: Container>
where
C::Element: UnsignedInteger,
{
data: C,
decomp_base_log: DecompositionBaseLog,
decomp_level_count: DecompositionLevelCount,
input_lwe_size: LweSize,
output_glwe_size: GlweSize,
output_polynomial_size: PolynomialSize,
ciphertext_modulus: CiphertextModulus<C::Element>,
}
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]>
for LwePublicFunctionalPackingKeyswitchKeyList<C>
{
fn as_ref(&self) -> &[T] {
self.data.as_ref()
}
}
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]>
for LwePublicFunctionalPackingKeyswitchKeyList<C>
{
fn as_mut(&mut self) -> &mut [T] {
self.data.as_mut()
}
}
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>>
LwePublicFunctionalPackingKeyswitchKeyList<C>
{
/// Create an [`LwePublicFunctionalPackingKeyswitchKeyList`] from an existing container.
///
/// # Note
///
/// This function only wraps a container in the appropriate type. If you want to generate keys
/// in the list you need to use
/// [`crate::core_crypto::algorithms::generate_lwe_public_functional_packing_keyswitch_key`] or
/// the parallel variant
/// [`crate::core_crypto::algorithms::par_generate_lwe_public_functional_packing_keyswitch_key`]
/// on the individual keys in the list.
///
/// This docstring exhibits [`LwePublicFunctionalPackingKeyswitchKeyList`] primitives usage.
///
/// ```
/// use tfhe::core_crypto::prelude::*;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for LwePublicFunctionalPackingKeyswitchKeyList creation
/// let glwe_size = GlweSize(2);
/// let polynomial_size = PolynomialSize(1024);
/// let decomp_base_log = DecompositionBaseLog(8);
/// let decomp_level_count = DecompositionLevelCount(3);
/// let input_lwe_dimension = LweDimension(600);
/// let lwe_pubfpksk_count = FunctionalPackingKeyswitchKeyCount(2);
/// let ciphertext_modulus = CiphertextModulus::new_native();
///
/// // Create a new LwePublicFunctionalPackingKeyswitchKeyList
/// let pubfpksk_list = LwePublicFunctionalPackingKeyswitchKeyList::new(
/// 0u64,
/// decomp_base_log,
/// decomp_level_count,
/// input_lwe_dimension,
/// glwe_size,
/// polynomial_size,
/// lwe_pubfpksk_count,
/// ciphertext_modulus,
/// );
///
/// assert_eq!(
/// pubfpksk_list.output_glwe_key_dimension(),
/// glwe_size.to_glwe_dimension()
/// );
/// assert_eq!(pubfpksk_list.output_glwe_size(), glwe_size);
/// assert_eq!(pubfpksk_list.output_polynomial_size(), polynomial_size);
/// assert_eq!(pubfpksk_list.decomposition_base_log(), decomp_base_log);
/// assert_eq!(
/// pubfpksk_list.decomposition_level_count(),
/// decomp_level_count
/// );
/// assert_eq!(
/// pubfpksk_list.input_lwe_size(),
/// input_lwe_dimension.to_lwe_size()
/// );
/// assert_eq!(pubfpksk_list.input_lwe_key_dimension(), input_lwe_dimension);
/// assert_eq!(pubfpksk_list.lwe_pubfpksk_count(), lwe_pubfpksk_count);
/// assert_eq!(pubfpksk_list.ciphertext_modulus(), ciphertext_modulus);
///
/// // Demonstrate how to recover the allocated container
/// let underlying_container: Vec<u64> = pubfpksk_list.into_container();
///
/// // Recreate a list using from_container
/// let pubfpksk_list = LwePublicFunctionalPackingKeyswitchKeyList::from_container(
/// underlying_container,
/// decomp_base_log,
/// decomp_level_count,
/// input_lwe_dimension.to_lwe_size(),
/// glwe_size,
/// polynomial_size,
/// ciphertext_modulus,
/// );
///
/// assert_eq!(
/// pubfpksk_list.output_glwe_key_dimension(),
/// glwe_size.to_glwe_dimension()
/// );
/// assert_eq!(pubfpksk_list.output_glwe_size(), glwe_size);
/// assert_eq!(pubfpksk_list.output_polynomial_size(), polynomial_size);
/// assert_eq!(pubfpksk_list.decomposition_base_log(), decomp_base_log);
/// assert_eq!(
/// pubfpksk_list.decomposition_level_count(),
/// decomp_level_count
/// );
/// assert_eq!(
/// pubfpksk_list.input_lwe_size(),
/// input_lwe_dimension.to_lwe_size()
/// );
/// assert_eq!(pubfpksk_list.input_lwe_key_dimension(), input_lwe_dimension);
/// assert_eq!(pubfpksk_list.lwe_pubfpksk_count(), lwe_pubfpksk_count);
/// assert_eq!(pubfpksk_list.ciphertext_modulus(), ciphertext_modulus);
/// ```
pub fn from_container(
container: C,
decomp_base_log: DecompositionBaseLog,
decomp_level_count: DecompositionLevelCount,
input_lwe_size: LweSize,
output_glwe_size: GlweSize,
output_polynomial_size: PolynomialSize,
ciphertext_modulus: CiphertextModulus<C::Element>,
) -> LwePublicFunctionalPackingKeyswitchKeyList<C> {
assert!(
container.container_len()
% lwe_pubfpksk_size(
input_lwe_size,
decomp_level_count,
output_glwe_size,
output_polynomial_size
)
== 0,
"The provided container length is not valid. \
It needs to be dividable by input_lwe_size * decomp_level_count * output_glwe_size * \
output_polynomial_size: {}. Got container length: {} and input_lwe_size: {input_lwe_size:?}\
decomp_level_count: {decomp_level_count:?}, output_glwe_size: {output_glwe_size:?}, \
output_polynomial_size: {output_polynomial_size:?}.",
lwe_pubfpksk_size(
input_lwe_size,
decomp_level_count,
output_glwe_size,
output_polynomial_size
),
container.container_len()
);
LwePublicFunctionalPackingKeyswitchKeyList {
data: container,
decomp_base_log,
decomp_level_count,
input_lwe_size,
output_glwe_size,
output_polynomial_size,
ciphertext_modulus,
}
}
/// Return the output key [`GlweDimension`] of the [`LwePublicFunctionalPackingKeyswitchKey`]
/// stored in the list.
///
/// See [`LwePublicFunctionalPackingKeyswitchKeyList::from_container`] for usage.
pub fn output_glwe_key_dimension(&self) -> GlweDimension {
self.output_glwe_size.to_glwe_dimension()
}
/// Return the output [`GlweSize`] of the [`LwePublicFunctionalPackingKeyswitchKey`] stored in
/// the list.
///
/// See [`LwePublicFunctionalPackingKeyswitchKeyList::from_container`] for usage.
pub fn output_glwe_size(&self) -> GlweSize {
self.output_glwe_size
}
/// Return the output [`PolynomialSize`] of the [`LwePublicFunctionalPackingKeyswitchKey`]
/// stored in the list.
///
/// See [`LwePublicFunctionalPackingKeyswitchKeyList::from_container`] for usage.
pub fn output_polynomial_size(&self) -> PolynomialSize {
self.output_polynomial_size
}
/// Return the input key [`LweDimension`] of the [`LwePublicFunctionalPackingKeyswitchKey`]
/// stored in the list.
///
/// See [`LwePublicFunctionalPackingKeyswitchKeyList::from_container`] for usage.
pub fn input_lwe_key_dimension(&self) -> LweDimension {
self.input_lwe_size.to_lwe_dimension()
}
/// Return the input [`LweSize`] of the [`LwePublicFunctionalPackingKeyswitchKey`]stored in the
/// list.
///
/// See [`LwePublicFunctionalPackingKeyswitchKeyList::from_container`] for usage.
pub fn input_lwe_size(&self) -> LweSize {
self.input_lwe_size
}
/// Return the [`DecompositionLevelCount`] of the [`LwePublicFunctionalPackingKeyswitchKey`]
/// stored in the list.
///
/// See [`LwePublicFunctionalPackingKeyswitchKeyList::from_container`] for usage.
pub fn decomposition_level_count(&self) -> DecompositionLevelCount {
self.decomp_level_count
}
/// Return the [`DecompositionBaseLog`] of the [`LwePublicFunctionalPackingKeyswitchKey`]
/// stored in the list.
///
/// See [`LwePublicFunctionalPackingKeyswitchKeyList::from_container`] for usage.
pub fn decomposition_base_log(&self) -> DecompositionBaseLog {
self.decomp_base_log
}
/// Return the number of elements in a [`LwePublicFunctionalPackingKeyswitchKey`] stored in
/// the list.
pub fn lwe_pubfpksk_size(&self) -> usize {
lwe_pubfpksk_size(
self.input_lwe_size,
self.decomp_level_count,
self.output_glwe_size,
self.output_polynomial_size,
)
}
/// Return the [`FunctionalPackingKeyswitchKeyCount`] of the
/// [`LwePublicFunctionalPackingKeyswitchKeyList`].
///
/// See [`LwePublicFunctionalPackingKeyswitchKeyList::from_container`] for usage.
pub fn lwe_pubfpksk_count(&self) -> FunctionalPackingKeyswitchKeyCount {
FunctionalPackingKeyswitchKeyCount(self.as_ref().container_len() / self.lwe_pubfpksk_size())
}
/// Return a view of the [`LwePublicFunctionalPackingKeyswitchKeyList`]. This is useful if an
/// algorithm takes a view by value.
pub fn as_view(&self) -> LwePublicFunctionalPackingKeyswitchKeyList<&'_ [Scalar]> {
LwePublicFunctionalPackingKeyswitchKeyList::from_container(
self.as_ref(),
self.decomp_base_log,
self.decomp_level_count,
self.input_lwe_size,
self.output_glwe_size,
self.output_polynomial_size,
self.ciphertext_modulus,
)
}
/// Consume the entity and return its underlying container.
///
/// See [`LwePublicFunctionalPackingKeyswitchKeyList::from_container`] for usage.
pub fn into_container(self) -> C {
self.data
}
/// Return the [`CiphertextModulus`] of the [`LwePublicFunctionalPackingKeyswitchKeyList`]
///
/// See [`LwePublicFunctionalPackingKeyswitchKeyList::from_container`] for usage.
pub fn ciphertext_modulus(&self) -> CiphertextModulus<C::Element> {
self.ciphertext_modulus
}
}
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>>
LwePublicFunctionalPackingKeyswitchKeyList<C>
{
/// Mutable variant of [`LwePublicFunctionalPackingKeyswitchKeyList::as_view`].
pub fn as_mut_view(&mut self) -> LwePublicFunctionalPackingKeyswitchKeyList<&'_ mut [Scalar]> {
let decomp_base_log = self.decomp_base_log;
let decomp_level_count = self.decomp_level_count;
let input_lwe_size = self.input_lwe_size;
let output_glwe_size = self.output_glwe_size;
let output_polynomial_size = self.output_polynomial_size;
let ciphertext_modulus = self.ciphertext_modulus;
LwePublicFunctionalPackingKeyswitchKeyList::from_container(
self.as_mut(),
decomp_base_log,
decomp_level_count,
input_lwe_size,
output_glwe_size,
output_polynomial_size,
ciphertext_modulus,
)
}
}
/// An [`LwePublicFunctionalPackingKeyswitchKeyList`] owning the memory for its own storage.
pub type LwePublicFunctionalPackingKeyswitchKeyListOwned<Scalar> =
LwePublicFunctionalPackingKeyswitchKeyList<Vec<Scalar>>;
impl<Scalar: UnsignedInteger> LwePublicFunctionalPackingKeyswitchKeyListOwned<Scalar> {
/// Allocate memory and create a new owned [`LwePublicFunctionalPackingKeyswitchKeyList`].
///
/// # Note
///
/// This function allocates a vector of the appropriate size and wraps it in the appropriate
/// type. If you want to generate keys in the list you need to use
/// [`crate::core_crypto::algorithms::generate_lwe_public_functional_packing_keyswitch_key`] or
/// the parallel variant
/// [`crate::core_crypto::algorithms::par_generate_lwe_public_functional_packing_keyswitch_key`]
/// on the individual keys in the list.
/// See [`LwePublicFunctionalPackingKeyswitchKeyList::from_container`] for usage.
#[allow(clippy::too_many_arguments)]
pub fn new(
fill_with: Scalar,
decomp_base_log: DecompositionBaseLog,
decomp_level_count: DecompositionLevelCount,
input_key_lwe_dimension: LweDimension,
output_glwe_size: GlweSize,
output_polynomial_size: PolynomialSize,
pubfpksk_count: FunctionalPackingKeyswitchKeyCount,
ciphertext_modulus: CiphertextModulus<Scalar>,
) -> LwePublicFunctionalPackingKeyswitchKeyListOwned<Scalar> {
LwePublicFunctionalPackingKeyswitchKeyListOwned::from_container(
vec![
fill_with;
pubfpksk_count.0
* lwe_pubfpksk_size(
input_key_lwe_dimension.to_lwe_size(),
decomp_level_count,
output_glwe_size,
output_polynomial_size
)
],
decomp_base_log,
decomp_level_count,
input_key_lwe_dimension.to_lwe_size(),
output_glwe_size,
output_polynomial_size,
ciphertext_modulus,
)
}
}
/// Metadata used in the [`CreateFrom`] implementation to create
/// [`LwePublicFunctionalPackingKeyswitchKeyList`] entities.
#[derive(Clone, Copy)]
pub struct LwePublicFunctionalPackingKeyswitchKeyListCreationMetadata<Scalar: UnsignedInteger>(
pub DecompositionBaseLog,
pub DecompositionLevelCount,
pub LweSize,
pub GlweSize,
pub PolynomialSize,
pub CiphertextModulus<Scalar>,
);
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CreateFrom<C>
for LwePublicFunctionalPackingKeyswitchKeyList<C>
{
type Metadata = LwePublicFunctionalPackingKeyswitchKeyListCreationMetadata<Scalar>;
#[inline]
fn create_from(from: C, meta: Self::Metadata) -> Self {
let LwePublicFunctionalPackingKeyswitchKeyListCreationMetadata(
decomp_base_log,
decomp_level_count,
input_lwe_size,
output_glwe_size,
output_polynomial_size,
ciphertext_modulus,
) = meta;
LwePublicFunctionalPackingKeyswitchKeyList::from_container(
from,
decomp_base_log,
decomp_level_count,
input_lwe_size,
output_glwe_size,
output_polynomial_size,
ciphertext_modulus,
)
}
}
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> ContiguousEntityContainer
for LwePublicFunctionalPackingKeyswitchKeyList<C>
{
type Element = C::Element;
type EntityViewMetadata = LwePublicFunctionalPackingKeyswitchKeyCreationMetadata<Scalar>;
type EntityView<'this> = LwePublicFunctionalPackingKeyswitchKey<&'this [Self::Element]>
where
Self: 'this;
type SelfViewMetadata = LwePublicFunctionalPackingKeyswitchKeyListCreationMetadata<Scalar>;
type SelfView<'this> = LwePublicFunctionalPackingKeyswitchKeyList<&'this [Self::Element]>
where
Self: 'this;
fn get_entity_view_creation_metadata(&self) -> Self::EntityViewMetadata {
LwePublicFunctionalPackingKeyswitchKeyCreationMetadata(
self.decomposition_base_log(),
self.decomposition_level_count(),
self.output_glwe_size(),
self.output_polynomial_size(),
self.ciphertext_modulus(),
)
}
fn get_entity_view_pod_size(&self) -> usize {
self.lwe_pubfpksk_size()
}
fn get_self_view_creation_metadata(&self) -> Self::SelfViewMetadata {
LwePublicFunctionalPackingKeyswitchKeyListCreationMetadata(
self.decomposition_base_log(),
self.decomposition_level_count(),
self.input_lwe_size(),
self.output_glwe_size(),
self.output_polynomial_size(),
self.ciphertext_modulus(),
)
}
}
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> ContiguousEntityContainerMut
for LwePublicFunctionalPackingKeyswitchKeyList<C>
{
type EntityMutView<'this> = LwePublicFunctionalPackingKeyswitchKey<&'this mut [Self::Element]>
where
Self: 'this;
type SelfMutView<'this> = LwePublicFunctionalPackingKeyswitchKeyList<&'this mut [Self::Element]>
where
Self: 'this;
}

View File

@@ -0,0 +1,410 @@
//! Module containing the definition of the LweTracePackingKeyswitchKey.
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
/// An [`LWE trace packing keyswitch key`](`LweTracePackingKeyswitchKey`).
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct LweTracePackingKeyswitchKey<C: Container>
where
C::Element: UnsignedInteger,
{
data: C,
decomp_base_log: DecompositionBaseLog,
decomp_level_count: DecompositionLevelCount,
input_lwe_size: LweSize,
output_glwe_size: GlweSize,
polynomial_size: PolynomialSize,
ciphertext_modulus: CiphertextModulus<C::Element>,
}
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]> for LweTracePackingKeyswitchKey<C> {
fn as_ref(&self) -> &[T] {
self.data.as_ref()
}
}
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]>
for LweTracePackingKeyswitchKey<C>
{
fn as_mut(&mut self) -> &mut [T] {
self.data.as_mut()
}
}
/// Return the number of elements in an encryption of an input [`LweSecretKey`] element for a
/// [`LweTracePackingKeyswitchKey`] given a [`DecompositionLevelCount`] and output
/// [`GlweSize`] and [`PolynomialSize`].
pub fn lwe_tpksk_input_key_element_encrypted_size(
decomp_level_count: DecompositionLevelCount,
output_glwe_size: GlweSize,
polynomial_size: PolynomialSize,
) -> usize {
// One ciphertext per level encrypted under the output key
decomp_level_count.0 * output_glwe_size.0 * polynomial_size.0
}
/// Return the number of elements in an [`LweTracePackingKeyswitchKey`] given a
/// [`DecompositionLevelCount`], output [`GlweSize`], and output [`PolynomialSize`].
pub fn lwe_tpksk_size(
decomp_level_count: DecompositionLevelCount,
output_glwe_size: GlweSize,
polynomial_size: PolynomialSize,
) -> usize {
output_glwe_size.to_glwe_dimension().0
* polynomial_size.log2().0
* lwe_tpksk_input_key_element_encrypted_size(
decomp_level_count,
output_glwe_size,
polynomial_size,
)
}
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> LweTracePackingKeyswitchKey<C> {
/// Create an [`LweTracePackingKeyswitchKey`] from an existing container.
///
/// # Note
///
/// This function only wraps a container in the appropriate type. If you want to generate an
/// [`LweTracePackingKeyswitchKey`] you need to use
/// [`crate::core_crypto::algorithms::generate_lwe_trace_packing_keyswitch_key`]
/// using this key as output.
///
/// This docstring exhibits [`LweTracePackingKeyswitchKey`] primitives usage.
///
/// ```
/// use tfhe::core_crypto::prelude::*;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for LweTracePackingKeyswitchKey creation
/// let lwe_size = LweSize(1001);
/// let glwe_size = GlweSize(2);
/// let polynomial_size = PolynomialSize(1024);
/// let decomp_base_log = DecompositionBaseLog(8);
/// let decomp_level_count = DecompositionLevelCount(3);
/// let ciphertext_modulus = CiphertextModulus::new_native();
///
/// // Create a new LweTracePackingKeyswitchKey
/// let tpksk = LweTracePackingKeyswitchKey::new(
/// 0u64,
/// decomp_base_log,
/// decomp_level_count,
/// lwe_size,
/// glwe_size,
/// polynomial_size,
/// ciphertext_modulus,
/// );
///
/// assert_eq!(
/// tpksk.output_glwe_key_dimension(),
/// glwe_size.to_glwe_dimension()
/// );
/// assert_eq!(tpksk.output_glwe_size(), glwe_size);
/// assert_eq!(tpksk.polynomial_size(), polynomial_size);
/// assert_eq!(tpksk.decomposition_base_log(), decomp_base_log);
/// assert_eq!(tpksk.decomposition_level_count(), decomp_level_count);
/// assert_eq!(tpksk.ciphertext_modulus(), ciphertext_modulus);
///
/// // Demonstrate how to recover the allocated container
/// let underlying_container: Vec<u64> = tpksk.into_container();
///
/// // Recreate a key using from_container
/// let tpksk = LweTracePackingKeyswitchKey::from_container(
/// underlying_container,
/// decomp_base_log,
/// decomp_level_count,
/// lwe_size,
/// glwe_size,
/// polynomial_size,
/// ciphertext_modulus,
/// );
///
/// assert_eq!(
/// tpksk.output_glwe_key_dimension(),
/// glwe_size.to_glwe_dimension()
/// );
/// assert_eq!(tpksk.input_lwe_size(), lwe_size);
/// assert_eq!(tpksk.output_glwe_size(), glwe_size);
/// assert_eq!(tpksk.polynomial_size(), polynomial_size);
/// assert_eq!(tpksk.decomposition_base_log(), decomp_base_log);
/// assert_eq!(tpksk.decomposition_level_count(), decomp_level_count);
/// assert_eq!(tpksk.ciphertext_modulus(), ciphertext_modulus);
/// ```
pub fn from_container(
container: C,
decomp_base_log: DecompositionBaseLog,
decomp_level_count: DecompositionLevelCount,
input_lwe_size: LweSize,
output_glwe_size: GlweSize,
polynomial_size: PolynomialSize,
ciphertext_modulus: CiphertextModulus<C::Element>,
) -> LweTracePackingKeyswitchKey<C> {
assert!(
container.container_len() > 0,
"Got an empty container to create an LweKeyswitchKey"
);
assert!(
container.container_len()
% lwe_tpksk_input_key_element_encrypted_size(
decomp_level_count,
output_glwe_size,
polynomial_size
)
== 0,
"The provided container length is not valid. \
It needs to be divisable by decomp_level_count * output_glwe_size * polynomial_size:\
{}. Got container length: {} and decomp_level_count: {decomp_level_count:?}, \
output_glwe_size: {output_glwe_size:?}, polynomial_size: \
{polynomial_size:?}.",
lwe_tpksk_input_key_element_encrypted_size(
decomp_level_count,
output_glwe_size,
polynomial_size
),
container.container_len()
);
LweTracePackingKeyswitchKey {
data: container,
decomp_base_log,
decomp_level_count,
input_lwe_size,
output_glwe_size,
polynomial_size,
ciphertext_modulus,
}
}
/// Return the output key [`GlweDimension`] of the [`LweTracePackingKeyswitchKey`].
///
/// See [`LweTracePackingKeyswitchKey::from_container`] for usage.
pub fn output_glwe_key_dimension(&self) -> GlweDimension {
self.output_glwe_size.to_glwe_dimension()
}
/// Return the output [`GlweSize`] of the [`LweTracePackingKeyswitchKey`].
///
/// See [`LweTracePackingKeyswitchKey::from_container`] for usage.
pub fn output_glwe_size(&self) -> GlweSize {
self.output_glwe_size
}
/// Return the output [`PolynomialSize`] of the [`LweTracePackingKeyswitchKey`].
///
/// See [`LweTracePackingKeyswitchKey::from_container`] for usage.
pub fn polynomial_size(&self) -> PolynomialSize {
self.polynomial_size
}
/// Return the input [`LweSize`] of the [`LweTracePackingKeyswitchKey`].
///
/// See [`LweTracePackingKeyswitchKey::from_container`] for usage.
pub fn input_lwe_size(&self) -> LweSize {
self.input_lwe_size
}
/// Return the [`DecompositionLevelCount`] of the [`LweTracePackingKeyswitchKey`].
///
/// See [`LweTracePackingKeyswitchKey::from_container`] for usage.
pub fn decomposition_level_count(&self) -> DecompositionLevelCount {
self.decomp_level_count
}
/// Return the [`DecompositionBaseLog`] of the [`LweTracePackingKeyswitchKey`].
///
/// See [`LweTracePackingKeyswitchKey::from_container`] for usage.
pub fn decomposition_base_log(&self) -> DecompositionBaseLog {
self.decomp_base_log
}
/// Return the number of elements in an encryption of an input [`LweSecretKey`] element of the
/// current [`LweTracePackingKeyswitchKey`].
pub fn input_key_element_encrypted_size(&self) -> usize {
lwe_tpksk_input_key_element_encrypted_size(
self.decomp_level_count,
self.output_glwe_size,
self.polynomial_size,
)
}
/// Return a view of the [`LweTracePackingKeyswitchKey`]. This is useful if an
/// algorithm takes a view by value.
pub fn as_view(&self) -> LweTracePackingKeyswitchKey<&'_ [Scalar]> {
LweTracePackingKeyswitchKey::from_container(
self.as_ref(),
self.decomp_base_log,
self.decomp_level_count,
self.input_lwe_size,
self.output_glwe_size,
self.polynomial_size,
self.ciphertext_modulus,
)
}
/// Consume the entity and return its underlying container.
///
/// See [`LweTracePackingKeyswitchKey::from_container`] for usage.
pub fn into_container(self) -> C {
self.data
}
/// Return the [`CiphertextModulus`] of the [`LweTracePackingKeyswitchKey`]
///
/// See [`LweTracePackingKeyswitchKey::from_container`] for usage.
pub fn ciphertext_modulus(&self) -> CiphertextModulus<C::Element> {
self.ciphertext_modulus
}
}
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> LweTracePackingKeyswitchKey<C> {
/// Mutable variant of [`LweTracePackingKeyswitchKey::as_view`].
pub fn as_mut_view(&mut self) -> LweTracePackingKeyswitchKey<&'_ mut [Scalar]> {
let decomp_base_log = self.decomp_base_log;
let decomp_level_count = self.decomp_level_count;
let input_lwe_size = self.input_lwe_size;
let output_glwe_size = self.output_glwe_size;
let polynomial_size = self.polynomial_size;
let ciphertext_modulus = self.ciphertext_modulus;
LweTracePackingKeyswitchKey::from_container(
self.as_mut(),
decomp_base_log,
decomp_level_count,
input_lwe_size,
output_glwe_size,
polynomial_size,
ciphertext_modulus,
)
}
}
/// An [`LweTracePackingKeyswitchKey`] owning the memory for its own storage.
pub type LweTracePackingKeyswitchKeyOwned<Scalar> = LweTracePackingKeyswitchKey<Vec<Scalar>>;
impl<Scalar: UnsignedInteger> LweTracePackingKeyswitchKeyOwned<Scalar> {
/// Create an [`LweTracePackingKeyswitchKey`] from an existing container.
///
/// # Note
///
/// This function allocates a vector of the appropriate size and wraps it in the appropriate
/// type. If you want to generate an [`LweTracePackingKeyswitchKey`] you need to use
/// [`crate::core_crypto::algorithms::generate_lwe_trace_packing_keyswitch_key`]
/// using this key as output.
///
/// See [`LweTracePackingKeyswitchKey::from_container`] for usage.
pub fn new(
fill_with: Scalar,
decomp_base_log: DecompositionBaseLog,
decomp_level_count: DecompositionLevelCount,
input_lwe_size: LweSize,
output_glwe_size: GlweSize,
polynomial_size: PolynomialSize,
ciphertext_modulus: CiphertextModulus<Scalar>,
) -> LweTracePackingKeyswitchKeyOwned<Scalar> {
LweTracePackingKeyswitchKeyOwned::from_container(
vec![fill_with; lwe_tpksk_size(decomp_level_count, output_glwe_size, polynomial_size)],
decomp_base_log,
decomp_level_count,
input_lwe_size,
output_glwe_size,
polynomial_size,
ciphertext_modulus,
)
}
}
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> ContiguousEntityContainer
for LweTracePackingKeyswitchKey<C>
{
type Element = C::Element;
type EntityViewMetadata = GlweCiphertextListCreationMetadata<Scalar>;
type EntityView<'this> = GlweCiphertextListView<'this, Self::Element>
where
Self: 'this;
type SelfViewMetadata = ();
// At the moment it does not make sense to return "sub" packing keyswitch keys. So we use a
// dummy placeholder type here.
type SelfView<'this> = DummyCreateFrom
where
Self: 'this;
fn get_entity_view_creation_metadata(&self) -> Self::EntityViewMetadata {
GlweCiphertextListCreationMetadata(
self.output_glwe_size,
self.polynomial_size,
self.ciphertext_modulus,
)
}
fn get_entity_view_pod_size(&self) -> usize {
self.input_key_element_encrypted_size() * self.output_glwe_size.to_glwe_dimension().0
}
/// Unimplemented for [`LweTracePackingKeyswitchKey`]. At the moment it does not
/// make sense to return "sub" packing keyswitch keys.
fn get_self_view_creation_metadata(&self) -> Self::SelfViewMetadata {
unimplemented!(
"This function is not supported for LweTracePackingKeyswitchKey. \
At the moment it does not make sense to return 'sub' packing keyswitch keys."
)
}
}
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> ContiguousEntityContainerMut
for LweTracePackingKeyswitchKey<C>
{
type EntityMutView<'this> = GlweCiphertextListMutView<'this, Self::Element>
where
Self: 'this;
// At the moment it does not make sense to return "sub" packing keyswitch keys. So we use a
// dummy placeholder type here.
type SelfMutView<'this> = DummyCreateFrom
where
Self: 'this;
}
/// Metadata used in the [`CreateFrom`] implementation to create
/// [`LweTracePackingKeyswitchKey`] entities.
#[derive(Clone, Copy)]
pub struct LweTracePackingKeyswitchKeyCreationMetadata<Scalar: UnsignedInteger>(
pub DecompositionBaseLog,
pub DecompositionLevelCount,
pub LweSize,
pub GlweSize,
pub PolynomialSize,
pub CiphertextModulus<Scalar>,
);
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CreateFrom<C>
for LweTracePackingKeyswitchKey<C>
{
type Metadata = LweTracePackingKeyswitchKeyCreationMetadata<Scalar>;
#[inline]
fn create_from(from: C, meta: Self::Metadata) -> LweTracePackingKeyswitchKey<C> {
let LweTracePackingKeyswitchKeyCreationMetadata(
decomp_base_log,
decomp_level_count,
input_lwe_size,
output_glwe_size,
polynomial_size,
ciphertext_modulus,
) = meta;
LweTracePackingKeyswitchKey::from_container(
from,
decomp_base_log,
decomp_level_count,
input_lwe_size,
output_glwe_size,
polynomial_size,
ciphertext_modulus,
)
}
}

View File

@@ -8,6 +8,8 @@ pub mod ggsw_ciphertext;
pub mod ggsw_ciphertext_list;
pub mod glwe_ciphertext;
pub mod glwe_ciphertext_list;
pub mod glwe_keyswitch_key;
pub mod glwe_relinearisation_key;
pub mod glwe_secret_key;
pub mod gsw_ciphertext;
pub mod lwe_bootstrap_key;
@@ -17,8 +19,11 @@ pub mod lwe_keyswitch_key;
pub mod lwe_multi_bit_bootstrap_key;
pub mod lwe_private_functional_packing_keyswitch_key;
pub mod lwe_private_functional_packing_keyswitch_key_list;
pub mod lwe_public_functional_packing_keyswitch_key;
pub mod lwe_public_functional_packing_keyswitch_key_list;
pub mod lwe_public_key;
pub mod lwe_secret_key;
pub mod lwe_trace_packing_keyswitch_key;
pub mod plaintext;
pub mod plaintext_list;
pub mod polynomial;
@@ -27,6 +32,7 @@ pub mod seeded_ggsw_ciphertext;
pub mod seeded_ggsw_ciphertext_list;
pub mod seeded_glwe_ciphertext;
pub mod seeded_glwe_ciphertext_list;
pub mod seeded_glwe_keyswitch_key;
pub mod seeded_lwe_bootstrap_key;
pub mod seeded_lwe_ciphertext;
pub mod seeded_lwe_ciphertext_list;
@@ -51,6 +57,8 @@ pub use ggsw_ciphertext::*;
pub use ggsw_ciphertext_list::*;
pub use glwe_ciphertext::*;
pub use glwe_ciphertext_list::*;
pub use glwe_keyswitch_key::*;
pub use glwe_relinearisation_key::*;
pub use glwe_secret_key::*;
pub use gsw_ciphertext::*;
pub use lwe_bootstrap_key::*;
@@ -60,8 +68,11 @@ pub use lwe_keyswitch_key::*;
pub use lwe_multi_bit_bootstrap_key::*;
pub use lwe_private_functional_packing_keyswitch_key::*;
pub use lwe_private_functional_packing_keyswitch_key_list::*;
pub use lwe_public_functional_packing_keyswitch_key::*;
pub use lwe_public_functional_packing_keyswitch_key_list::*;
pub use lwe_public_key::*;
pub use lwe_secret_key::*;
pub use lwe_trace_packing_keyswitch_key::*;
pub use plaintext::*;
pub use plaintext_list::*;
pub use polynomial::*;
@@ -70,6 +81,7 @@ pub use seeded_ggsw_ciphertext::*;
pub use seeded_ggsw_ciphertext_list::*;
pub use seeded_glwe_ciphertext::*;
pub use seeded_glwe_ciphertext_list::*;
pub use seeded_glwe_keyswitch_key::*;
pub use seeded_lwe_bootstrap_key::*;
pub use seeded_lwe_ciphertext::*;
pub use seeded_lwe_ciphertext_list::*;

View File

@@ -0,0 +1,379 @@
/*
//! Module containing the definition of the SeededGlweKeyswitchKey.
use crate::core_crypto::algorithms::*;
use crate::core_crypto::commons::math::random::{ActivatedRandomGenerator, CompressionSeed};
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
/// A [`seeded GLWE keyswitch key`](`SeededGlweKeyswitchKey`).
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct SeededGlweKeyswitchKey<C: Container> {
data: C,
decomp_base_log: DecompositionBaseLog,
decomp_level_count: DecompositionLevelCount,
output_glwe_size: GlweSize,
poly_size: PolynomialSize,
compression_seed: CompressionSeed,
}
impl<T, C: Container<Element = T>> AsRef<[T]> for SeededGlweKeyswitchKey<C> {
fn as_ref(&self) -> &[T] {
self.data.as_ref()
}
}
impl<T, C: ContainerMut<Element = T>> AsMut<[T]> for SeededGlweKeyswitchKey<C> {
fn as_mut(&mut self) -> &mut [T] {
self.data.as_mut()
}
}
/// Return the number of elements in an encryption of an input [`GlweSecretKey`] element for a
/// [`SeededGlweKeyswitchKey`] given a [`DecompositionLevelCount`] and output [`GlweSize`].
pub fn seeded_glwe_keyswitch_key_input_key_element_encrypted_size(
decomp_level_count: DecompositionLevelCount,
) -> usize {
// One seeded ciphertext per level
decomp_level_count.0
}
impl<Scalar, C: Container<Element = Scalar>> SeededGlweKeyswitchKey<C> {
/// Create an [`SeededGlweKeyswitchKey`] from an existing container.
///
/// # Note
///
/// This function only wraps a container in the appropriate type. If you want to generate an
/// [`SeededGlweKeyswitchKey`] you need to call
/// [`crate::core_crypto::algorithms::generate_seeded_glwe_keyswitch_key`] using this key as
/// output.
///
/// This docstring exhibits [`SeededGlweKeyswitchKey`] primitives usage.
///
/// ```
/// use tfhe::core_crypto::prelude::*;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for SeededLweKeyswitchKey creation
/// let input_lwe_dimension = LweDimension(600);
/// let output_lwe_dimension = LweDimension(1024);
/// let decomp_base_log = DecompositionBaseLog(4);
/// let decomp_level_count = DecompositionLevelCount(5);
///
/// // Get a seeder
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
///
/// // Create a new SeededLweKeyswitchKey
/// let lwe_ksk = SeededLweKeyswitchKey::new(
/// 0u64,
/// decomp_base_log,
/// decomp_level_count,
/// input_lwe_dimension,
/// output_lwe_dimension,
/// seeder.seed().into(),
/// );
///
/// assert_eq!(lwe_ksk.decomposition_base_log(), decomp_base_log);
/// assert_eq!(lwe_ksk.decomposition_level_count(), decomp_level_count);
/// assert_eq!(lwe_ksk.input_key_lwe_dimension(), input_lwe_dimension);
/// assert_eq!(lwe_ksk.output_key_lwe_dimension(), output_lwe_dimension);
/// assert_eq!(
/// lwe_ksk.output_lwe_size(),
/// output_lwe_dimension.to_lwe_size()
/// );
///
/// let compression_seed = lwe_ksk.compression_seed();
///
/// // Demonstrate how to recover the allocated container
/// let underlying_container: Vec<u64> = lwe_ksk.into_container();
///
/// // Recreate a secret key using from_container
/// let lwe_ksk = SeededLweKeyswitchKey::from_container(
/// underlying_container,
/// decomp_base_log,
/// decomp_level_count,
/// output_lwe_dimension.to_lwe_size(),
/// compression_seed,
/// );
///
/// assert_eq!(lwe_ksk.decomposition_base_log(), decomp_base_log);
/// assert_eq!(lwe_ksk.decomposition_level_count(), decomp_level_count);
/// assert_eq!(lwe_ksk.input_key_lwe_dimension(), input_lwe_dimension);
/// assert_eq!(lwe_ksk.output_key_lwe_dimension(), output_lwe_dimension);
/// assert_eq!(
/// lwe_ksk.output_lwe_size(),
/// output_lwe_dimension.to_lwe_size()
/// );
///
/// let lwe_ksk = lwe_ksk.decompress_into_lwe_keyswitch_key();
///
/// assert_eq!(lwe_ksk.decomposition_base_log(), decomp_base_log);
/// assert_eq!(lwe_ksk.decomposition_level_count(), decomp_level_count);
/// assert_eq!(lwe_ksk.input_key_lwe_dimension(), input_lwe_dimension);
/// assert_eq!(lwe_ksk.output_key_lwe_dimension(), output_lwe_dimension);
/// assert_eq!(
/// lwe_ksk.output_lwe_size(),
/// output_lwe_dimension.to_lwe_size()
/// );
/// ```
pub fn from_container(
container: C,
decomp_base_log: DecompositionBaseLog,
decomp_level_count: DecompositionLevelCount,
output_glwe_size: GlweSize,
poly_size: PolynomialSize,
compression_seed: CompressionSeed,
) -> Self {
assert!(
container.container_len() > 0,
"Got an empty container to create an SeededLweKeyswitchKey"
);
assert!(
container.container_len() % (decomp_level_count.0) == 0,
"The provided container length is not valid. \
It needs to be dividable by decomp_level_count: {}. \
Got container length: {} and decomp_level_count: {decomp_level_count:?}.",
decomp_level_count.0,
container.container_len()
);
SeededGlweKeyswitchKey {
data: container,
decomp_base_log,
decomp_level_count,
output_glwe_size,
poly_size,
compression_seed,
}
}
/// Return the [`DecompositionBaseLog`] of the [`SeededGlweKeyswitchKey`].
///
/// See [`SeededGlweKeyswitchKey::from_container`] for usage.
pub fn decomposition_base_log(&self) -> DecompositionBaseLog {
self.decomp_base_log
}
/// Return the [`DecompositionLevelCount`] of the [`SeededGlweKeyswitchKey`].
///
/// See [`SeededGlweKeyswitchKey::from_container`] for usage.
pub fn decomposition_level_count(&self) -> DecompositionLevelCount {
self.decomp_level_count
}
/// Return the input [`GlweDimension`] of the [`SeededGlweKeyswitchKey`].
///
/// See [`SeededGlweKeyswitchKey::from_container`] for usage.
pub fn input_key_glwe_dimension(&self) -> GlweDimension {
GlweDimension(self.data.container_len() / (self.seeded_input_key_element_encrypted_size()
* self.input_poly_size))
}
/// Return the input [`PolynomialSize`] of the [`SeededGlweKeyswitchKey`].
///
/// See [`SeededGlweKeyswitchKey::from_container`] for usage.
pub fn polynomial_size(&self) -> PolynomialSize {
self.poly_size
}
/// Return the output [`GlweDimension`] of the [`SeededGlweKeyswitchKey`].
///
/// See [`SeededGlweKeyswitchKey::from_container`] for usage.
pub fn output_key_glwe_dimension(&self) -> GlweDimension {
self.output_glwe_size.to_glwe_dimension()
}
/// Return the output [`GlweSize`] of the [`SeededGlweKeyswitchKey`].
///
/// See [`SeededGlweKeyswitchKey::from_container`] for usage.
pub fn output_glwe_size(&self) -> GlweSize {
self.output_glwe_size
}
/// Return the output [`CompressionSeed`] of the [`SeededGlweKeyswitchKey`].
///
/// See [`SeededGlweKeyswitchKey::from_container`] for usage.
pub fn compression_seed(&self) -> CompressionSeed {
self.compression_seed
}
/// Return the number of elements in an encryption of an input [`GlweSecretKey`] element of the
/// current [`SeededGlweKeyswitchKey`].
pub fn seeded_input_key_element_encrypted_size(&self) -> usize {
seeded_glwe_keyswitch_key_input_key_element_encrypted_size(self.decomp_level_count)
}
/// Return a view of the [`SeededGlweKeyswitchKey`]. This is useful if an algorithm takes a view
/// by value.
pub fn as_view(&self) -> SeededGlweKeyswitchKey<&'_ [Scalar]> {
SeededGlweKeyswitchKey::from_container(
self.as_ref(),
self.decomp_base_log,
self.decomp_level_count,
self.output_glwe_size,
self.poly_size,
self.compression_seed,
)
}
/// Consume the entity and return its underlying container.
///
/// See [`SeededGlweKeyswitchKey::from_container`] for usage.
pub fn into_container(self) -> C {
self.data
}
/// Consume the [`SeededGlweKeyswitchKey`] and decompress it into a standard
/// [`GlweKeyswitchKey`].
///
/// See [`SeededGlweKeyswitchKey::from_container`] for usage.
pub fn decompress_into_glwe_keyswitch_key(self) -> GlweKeyswitchKeyOwned<Scalar>
where
Scalar: UnsignedTorus,
{
let mut decompressed_ksk = GlweKeyswitchKeyOwned::new(
Scalar::ZERO,
self.decomposition_base_log(),
self.decomposition_level_count(),
self.input_key_glwe_dimension(),
self.output_key_glwe_dimension(),
self.polynomial_size(),
);
decompress_seeded_glwe_keyswitch_key::<_, _, _, ActivatedRandomGenerator>(
&mut decompressed_ksk,
&self,
);
decompressed_ksk
}
pub fn as_seeded_glwe_ciphertext_list(&self) -> SeededGlweCiphertextListView<'_, Scalar> {
SeededGlweCiphertextListView::from_container(
self.as_ref(),
self.output_glwe_size(),
self.polynomial_size(),
self.compression_seed(),
)
}
}
impl<Scalar, C: ContainerMut<Element = Scalar>> SeededGlweKeyswitchKey<C> {
/// Mutable variant of [`SeededGlweKeyswitchKey::as_view`].
pub fn as_mut_view(&mut self) -> SeededGlweKeyswitchKey<&'_ mut [Scalar]> {
let decomp_base_log = self.decomp_base_log;
let decomp_level_count = self.decomp_level_count;
let output_glwe_size = self.output_glwe_size;
let poly_size = self.poly_size;
let compression_seed = self.compression_seed;
SeededGlweKeyswitchKey::from_container(
self.as_mut(),
decomp_base_log,
decomp_level_count,
output_glwe_size,
poly_size,
compression_seed,
)
}
pub fn as_mut_seeded_glwe_ciphertext_list(
&mut self,
) -> SeededGlweCiphertextListMutView<'_, Scalar> {
let output_glwe_size = self.output_glwe_size();
let poly_size = self.polynomial_size();
let compression_seed = self.compression_seed();
SeededGlweCiphertextListMutView::from_container(
self.as_mut(),
output_glwe_size,
poly_size,
compression_seed,
)
}
}
/// A [`SeededGlweKeyswitchKey`] owning the memory for its own storage.
pub type SeededGlweKeyswitchKeyOwned<Scalar> = SeededGlweKeyswitchKey<Vec<Scalar>>;
impl<Scalar: Copy> SeededGlweKeyswitchKeyOwned<Scalar> {
/// Allocate memory and create a new owned [`SeededGlweKeyswitchKey`].
///
/// # Note
///
/// This function allocates a vector of the appropriate size and wraps it in the appropriate
/// type. If you want to generate an [`SeededGlweKeyswitchKey`] you need to call
/// [`crate::core_crypto::algorithms::generate_seeded_glwe_keyswitch_key`] using this key as
/// output.
///
/// See [`SeededGlweKeyswitchKey::from_container`] for usage.
pub fn new(
fill_with: Scalar,
decomp_base_log: DecompositionBaseLog,
decomp_level_count: DecompositionLevelCount,
input_key_glwe_dimension: GlweDimension,
output_key_glwe_dimension: GlweDimension,
polynomial_size: PolynomialSize,
compression_seed: CompressionSeed,
) -> SeededGlweKeyswitchKeyOwned<Scalar> {
SeededGlweKeyswitchKeyOwned::from_container(
vec![
fill_with;
input_key_glwe_dimension.0 * polynomial_size.0
* seeded_lwe_keyswitch_key_input_key_element_encrypted_size(decomp_level_count,)
],
decomp_base_log,
decomp_level_count,
output_key_glwe_dimension.to_glwe_size(),
polynomial_size,
compression_seed,
)
}
}
impl<C: Container> ContiguousEntityContainer for SeededGlweKeyswitchKey<C> {
type Element = C::Element;
type EntityViewMetadata = SeededGlweCiphertextListCreationMetadata;
type EntityView<'this> = SeededGlweCiphertextListView<'this, Self::Element>
where
Self: 'this;
type SelfViewMetadata = ();
// At the moment it does not make sense to return "sub" keyswitch keys. So we use a dummy
// placeholder type here.
type SelfView<'this> = DummyCreateFrom
where
Self: 'this;
fn get_entity_view_creation_metadata(&self) -> SeededGlweCiphertextListCreationMetadata {
SeededGlweCiphertextListCreationMetadata(self.output_glwe_size(), self.compression_seed())
}
fn get_entity_view_pod_size(&self) -> usize {
self.seeded_input_key_element_encrypted_size()
}
/// Unimplemented for [`SeededGlweKeyswitchKey`]. At the moment it does not make sense to
/// return "sub" keyswitch keys.
fn get_self_view_creation_metadata(&self) {
unimplemented!(
"This function is not supported for SeededLweKeyswitchKey. \
At the moment it does not make sense to return 'sub' keyswitch keys."
)
}
}
impl<C: ContainerMut> ContiguousEntityContainerMut for SeededGlweKeyswitchKey<C> {
type EntityMutView<'this> = SeededGlweCiphertextListMutView<'this, Self::Element>
where
Self: 'this;
// At the moment it does not make sense to return "sub" keyswitch keys. So we use a dummy
// placeholder type here.
type SelfMutView<'this> = DummyCreateFrom
where
Self: 'this;
}
*/

View File

@@ -4,7 +4,7 @@
//! Having `tfhe::core_crypto::prelude::*;` should be enough to start using the lib.
pub use super::algorithms::{
add_external_product_assign, polynomial_algorithms, slice_algorithms, *,
polynomial_algorithms, slice_algorithms, *,
};
pub use super::commons::computation_buffers::ComputationBuffers;
pub use super::commons::dispersion::*;