Skip to main content

poulpy_core/default/encryption/
mod.rs

1//! Secret-key and public-key encryption of ciphertexts and evaluation keys.
2//!
3//! This module provides traits and implementations for encrypting various
4//! lattice-based cryptographic objects, including:
5//!
6//! - **Ciphertexts**: [`GLWEEncryptSk`], [`GLWEEncryptPk`], [`GGLWEEncryptSk`],
7//!   [`GGSWEncryptSk`], [`LWEEncryptSk`] for encrypting plaintexts under
8//!   GLWE, GGLWE, GGSW, and LWE schemes.
9//!
10//! - **Key-switching keys**: [`GLWESwitchingKeyEncryptSk`], [`LWESwitchingKeyEncrypt`],
11//!   [`GLWEToLWESwitchingKeyEncryptSk`], [`LWEToGLWESwitchingKeyEncryptSk`] for
12//!   generating keys that enable switching between different secret keys or
13//!   between LWE and GLWE domains.
14//!
15//! - **Evaluation keys**: [`GLWEAutomorphismKeyEncryptSk`], [`GLWETensorKeyEncryptSk`],
16//!   [`GGLWEToGGSWKeyEncryptSk`] for generating keys used in automorphism,
17//!   tensor product, and GGLWE-to-GGSW conversion operations.
18//!
19//! - **Public keys**: [`GLWEPublicKeyGenerate`] for generating GLWE public keys
20//!   from secret keys.
21//!
22//! Encryption methods follow a consistent pattern with PRNG sources:
23//! - `source_xa`: source for mask/randomness sampling
24//! - `source_xe`: source for error/noise sampling
25//! - `source_xu`: source for uniform sampling (used in public-key encryption)
26//!
27//! Scratch space requirements for each operation can be queried via companion
28//! `*_tmp_bytes` methods.
29
30#![allow(clippy::too_many_arguments)]
31
32pub mod compressed;
33pub mod gglwe;
34pub mod gglwe_to_ggsw_key;
35pub mod ggsw;
36pub mod glwe;
37pub mod glwe_automorphism_key;
38pub mod glwe_public_key;
39pub mod glwe_switching_key;
40pub mod glwe_tensor_key;
41pub mod glwe_to_lwe_key;
42pub mod lwe;
43pub mod lwe_switching_key;
44pub mod lwe_to_glwe_key;
45
46pub use crate::api::{EncryptionInfos, GGSWEncryptSk, GLWEEncryptSk, GLWEMaskFill, LWEFillMask};
47pub use compressed::*;
48pub use gglwe::*;
49pub use gglwe_to_ggsw_key::*;
50pub use ggsw::*;
51pub use glwe::*;
52pub use glwe_automorphism_key::*;
53pub use glwe_public_key::*;
54pub use glwe_switching_key::*;
55pub use glwe_tensor_key::*;
56pub use glwe_to_lwe_key::*;
57pub use lwe::*;
58pub use lwe_switching_key::*;
59pub use lwe_to_glwe_key::*;
60use poulpy_hal::layouts::NoiseInfos;
61
62use crate::layouts::{GGLWEInfos, GGSWInfos, GLWEInfos, LWEInfos, TorusPrecision};
63use anyhow::Result;
64
65/// Standard deviation of the discrete Gaussian distribution used for error sampling
66/// during encryption. Set to 3.2.
67pub const DEFAULT_SIGMA_XE: f64 = 3.2;
68
69/// Truncation bound for the discrete Gaussian error distribution, defined as 6.0 * [DEFAULT_SIGMA_XE].
70/// Samples are rejected if their absolute value exceeds this bound.
71pub const DEFAULT_BOUND_XE: f64 = 6.0 * DEFAULT_SIGMA_XE;
72
73#[derive(Debug)]
74pub struct EncryptionLayout<L> {
75    pub layout: L,
76    pub noise: NoiseInfos,
77}
78
79impl<L: LWEInfos> EncryptionLayout<L> {
80    pub fn new(layout: L, noise: NoiseInfos) -> Result<Self> {
81        anyhow::ensure!(
82            noise.k <= layout.k().as_usize(),
83            "k_xe: {} > layout.k(): {}",
84            noise.k,
85            layout.k()
86        );
87        Ok(Self { layout, noise })
88    }
89
90    pub fn new_from_default_sigma(layout: L) -> Result<Self> {
91        // Place the error at the object's full precision `k` (the physical
92        // bottom limb). For key layouts `k()` already returns the total
93        // `dnum*dsize*base2k + k_aux`, so the guard region is properly encrypted.
94        let noise = NoiseInfos::new(layout.k().as_usize(), DEFAULT_SIGMA_XE, DEFAULT_BOUND_XE)?;
95        Self::new(layout, noise)
96    }
97}
98
99impl<L> EncryptionInfos for EncryptionLayout<L> {
100    fn noise_infos(&self) -> NoiseInfos {
101        self.noise
102    }
103}
104
105impl EncryptionInfos for NoiseInfos {
106    fn noise_infos(&self) -> NoiseInfos {
107        *self
108    }
109}
110
111impl<L: LWEInfos> LWEInfos for EncryptionLayout<L> {
112    fn base2k(&self) -> crate::layouts::Base2K {
113        self.layout.base2k()
114    }
115
116    fn n(&self) -> crate::layouts::Degree {
117        self.layout.n()
118    }
119
120    fn max_size(&self) -> usize {
121        self.layout.max_size()
122    }
123
124    fn size(&self) -> usize {
125        self.layout.size()
126    }
127
128    fn k(&self) -> TorusPrecision {
129        self.layout.k()
130    }
131}
132
133impl<L: GLWEInfos> GLWEInfos for EncryptionLayout<L> {
134    fn rank(&self) -> crate::layouts::Rank {
135        self.layout.rank()
136    }
137}
138
139impl<L: GGLWEInfos> GGLWEInfos for EncryptionLayout<L> {
140    fn k_aux(&self) -> crate::layouts::TorusPrecision {
141        self.layout.k_aux()
142    }
143
144    fn dnum(&self) -> crate::layouts::Dnum {
145        self.layout.dnum()
146    }
147
148    fn dsize(&self) -> crate::layouts::Dsize {
149        self.layout.dsize()
150    }
151
152    fn rank_in(&self) -> crate::layouts::Rank {
153        self.layout.rank_in()
154    }
155
156    fn rank_out(&self) -> crate::layouts::Rank {
157        self.layout.rank_out()
158    }
159}
160
161impl<L: GGSWInfos> GGSWInfos for EncryptionLayout<L> {
162    fn k_aux(&self) -> crate::layouts::TorusPrecision {
163        self.layout.k_aux()
164    }
165
166    fn dnum(&self) -> crate::layouts::Dnum {
167        self.layout.dnum()
168    }
169
170    fn dsize(&self) -> crate::layouts::Dsize {
171        self.layout.dsize()
172    }
173}