poulpy/poulpy-core/README.md
poulpy-core/README.mdRaw
1# 🐙 Poulpy-Core
2
3**Poulpy-Core** is a Rust crate built on **`poulpy-hal`**, providing scheme- and backend-agnostic RLWE-based homomorphic encryption building blocks.
4
5## Getting Started
6
7```rust
8use poulpy_backend::cpu_spqlios::FFT64;
9use poulpy_core::{
10 GLWEOperations, SIGMA,
11 layouts::{
12 GLWECiphertext, GLWEPlaintext, GLWESecret, Infos,
13 prepared::{GLWESecretPrepared, PrepareAlloc},
14 },
15};
16use poulpy_hal::{
17 api::{ModuleNew, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxFillUniform},
18 layouts::{Module, ScratchOwned},
19 source::Source,
20};
21
22fn main() {
23 // Ring degree
24 let log_n: usize = 10;
25
26 let n: usize = 1<<log_n;
27
28 // Base-2-k (implicit digit decomposition)
29 let basek: usize = 14;
30
31 // Ciphertext Torus precision (equivalent to ciphertext modulus)
32 let k_ct: usize = 27;
33
34 // Plaintext Torus precision (equivament to plaintext modulus)
35 let k_pt: usize = basek;
36
37 // GLWE rank
38 let rank: usize = 1;
39
40 // Instantiate Module (DFT Tables)
41 let module: Module<FFT64> = Module::<FFT64>::new(n as u64);
42
43 // Allocates ciphertext & plaintexts
44 let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(n, basek, k_ct, rank);
45 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(n, basek, k_pt);
46 let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(n, basek, k_pt);
47
48 // CPRNG
49 let mut source_xs: Source = Source::new([0u8; 32]);
50 let mut source_xe: Source = Source::new([1u8; 32]);
51 let mut source_xa: Source = Source::new([2u8; 32]);
52
53 // Scratch space
54 let mut scratch: ScratchOwned<FFT64> = ScratchOwned::alloc(
55 GLWECiphertext::encrypt_sk_scratch_space(&module, n, basek, ct.k())
56 | GLWECiphertext::decrypt_scratch_space(&module, n, basek, ct.k()),
57 );
58
59 // Generate secret-key
60 let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(n, rank);
61 sk.fill_ternary_prob(0.5, &mut source_xs);
62
63 // Backend-prepared secret
64 let sk_prepared: GLWESecretPrepared<Vec<u8>, FFT64> = sk.prepare_alloc(&module, scratch.borrow());
65
66 // Uniform plaintext
67 module.vec_znx_fill_uniform(basek, &mut pt_want.data, 0, k_pt, &mut source_xa);
68
69 // Encryption
70 ct.encrypt_sk(
71 &module,
72 &pt_want,
73 &sk_prepared,
74 &mut source_xa,
75 &mut source_xe,
76 scratch.borrow(),
77 );
78
79 // Decryption
80 ct.decrypt(&module, &mut pt_have, &sk_prepared, scratch.borrow());
81
82 // Diff between pt - Dec(Enc(pt))
83 pt_want.sub_inplace_ab(&module, &pt_have);
84
85 // Ideal vs. actual noise
86 let noise_have: f64 = pt_want.data.std(basek, 0) * (ct.k() as f64).exp2();
87 let noise_want: f64 = SIGMA;
88
89 // Check
90 assert!(noise_have <= noise_want + 0.2);
91}
92```
93
94## Layouts
95
96This crate defines three categories of layouts for `LWE`, `GLWE`, `GGLWE`, and `GGSW` objects (and their derivatives), all instantiated using **`poulpy-hal`** layouts. Each serves a distinct purpose:
97
98* **Standard** → Front-end, serializable layouts. These are backend-agnostic and act as inputs/outputs of computations (e.g., `GGLWEAutomorphismKey`).
99* **Compressed** → Compact serializable variants of the standard layouts. They are not usable for computation but significantly reduce storage size (e.g., `GGLWEAutomorphismKeyCompressed`).
100* **Prepared** → Backend-optimized, opaque layouts used only for computation (write-only). These store preprocessed data for efficient execution on a specific backend (e.g., `GGLWEAutomorphismKeyPrepared`).
101
102All **standard** and **compressed** layouts implement the `WriterTo` and `ReaderFrom` traits, enabling straightforward serialization/deserialization with any type implementing `Write` or `Read`:
103
104```rust
105pub trait WriterTo {
106 fn write_to<W: Write>(&self, writer: &mut W) -> Result<()>;
107}
108
109pub trait ReaderFrom {
110 fn read_from<R: Read>(&mut self, reader: &mut R) -> Result<()>;
111}
112```
113
114### Example Workflow
115
116```mermaid
117flowchart TD
118 A[GGLWEAutomorphismKeyCompressed]-->|decompress|B[GGLWEAutomorphismKey]-->|prepare|C[GGLWEAutomorphismKeyPrepared]
119```
120
121Equivalent Rust:
122
123```rust
124let mut atk_compressed: GGLWEAutomorphismKeyCompressed<Vec<u8>> =
125 GGLWEAutomorphismKeyCompressed::alloc(...);
126let mut atk: GGLWEAutomorphismKey<Vec<u8>> =
127 GGLWEAutomorphismKey::alloc(...);
128atk.decompress(module, &atk_compressed);
129let mut atk_prep: GGLWEAutomorphismKeyPrepared<Vec<u8>, B> = GLWESecretPrepared<Vec<u8>, B> = atk.prepare_alloc(...);
130```
131
132---
133
134## Encryption & Decryption
135
136* **Encryption** → Supported for all **standard** and **compressed** layouts.
137* **Decryption** → Only directly available for `LWECiphertext` and `GLWECiphertext`.
138 However, it remains naturally usable on `GGLWE` and `GGSW` objects, since these are vectors/matrices of `GLWECiphertext`.
139
140```rust
141let mut atk: GGLWEAutomorphismKey<Vec<u8>> =
142 GGLWEAutomorphismKey::alloc(...);
143atk.encrypt_sk(...);
144atk.at(row, 0).decrypt(...);
145```
146## Keyswitching, Automorphism & External Product
147
148Keyswitching, automorphisms, and external products are supported for all ciphertext types where they are well-defined.
149This includes subtypes such as `GGLWEAutomorphismKey`.
150
151For example:
152
153```rust
154atk.external_product(...);
155ggsw.automorphism(...);
156```
157
158---
159
160## Additional Features
161
162* Ciphertexts: `LWE` and `GLWE`
163* `GLWE` ring packing
164* `GLWE` trace
165* Noise analysis for `GLWE`, `GGLWE`, `GGSW`
166* Basic operations over `GLWE` ciphertexts and plaintexts
167
168---
169
170## Tests
171
172A fully generic test suite is available in [`src/tests/generics`](./src/tests/generics).