-
Notifications
You must be signed in to change notification settings - Fork 16
Cingulata
Cingulata, previously known as Armadillo (eprint), is a compiler that translates high-level programs written in C++ (against the Cingulata API) into circuits to be executed using FHE. It bundles its own implementation of the BFV scheme, and also has partial support for the TFHE library.
The compiler is bundled with a runtime environment that supports an offline mode (BFV only) and an online mode (TFHE only). In the offline mode, the given input C++ program is transformed into its equivalent Boolean circuit, optimized by the ABC tool, and finally executed. The online mode can be compared to an on-the-fly execution in that the input program is executed directly using a low-level implementation of the TFHE scheme.
The following describes how programs for BFV are compiled by Cingulata. The whole compilation process is fully automated using cmake/make files, and as such the following steps are enough to build Cingulata including its examples:
git clone https://github.com/CEA-LIST/Cingulata.git
cd Cingulata
mkdir build; cd "$_"
cmake ..
make
As an example for the following explanation, we consider the cardio
test program that is part of Cingulata and consists of two files:
-
cardio.cxx
- The program's logic. It uses theBitTracker
class to record operations to be transformed into their equivalent circuit at run-time. -
run.sh.in
- A script template that automates key generation, input data encryption, execution, and decryption.
During the cmake
step, the Makefiles are created that contain the commands required for building and optimizing the circuit. Also, the run.sh.in
script template is configured, i.e., placeholder variables (e.g., paths) that are formatted as @KEY_NAME@
are replaced by concrete values, resulting in the executable file run.sh
.
Executing the make
command starts the actual build process. Essential part of it is building the helper utilities (apps/
directory) for encoding data into binary numbers (helper
), en- and decryption (encrypt
/decrypt
), key generation (generate_keys
), and homomorphic evaluation (dyn_omp
).
Furthermore, the program cardio.cxx
is built and automatically executed, resulting in a circuit file bfv-cardio.blif
. Thereafter, a Python script is executed that invokes the ABC circuit optimizer on that circuit, resulting in an optimized circuit bfv-cardio-opt.blif
. A helper script (selectParams.sh
) then uses CinguParam to derive suitable parameters for the circuit. These are stored in the fhe_params.xml
file.
After the build process finished, the user can execute the run.sh
file to run the program:
This first generates the required FHE keys – a public key (fhe_key.pk
), a private key (fhe_key.sk
), and an evaluation key (fhe_key.evk
).
Then, the input values that are hard-coded in the script (run.sh
) are encoded as binary numbers and encrypted by bitwise-XOR with the hard-coded Kreyvium keystream.
A clear-text representation of that is written into clear_data.data
for debugging purposes.
The ciphertexts that serve as the circuit inputs are written into the input/
directory.
This directory contains a ciphertext file for each bit of an input parameter, e.g., i:ks_X_Y
denotes the Y-th bit of the X-th input parameter.
In the cardio example, each input value is encrypted using a 8 bit binary encoding.
Afterward, the program is homomorphically executed over the encrypted inputs and the encrypted result is written into the output/
directory in a bit-encoded style, similar as the input.
Finally, the execution's result is decrypted and printed to the screen.
There exist multiple circuits for the primitive operations (e.g., addition, multiplication).
In the circuit generation file it is possible to choose between a multiplicative depth-optimized circuit (IntOpGenDepth
) or a low-sized circuit (IntOpGenSize
).
For example, IntOpGenDepth
uses the following circuits for the primitive operations:
namespace cingulata
{
class IntOpGenDepth : public IIntOpGen {
...
private:
int_ops::SklanskyAdder m_add;
int_ops::Negate m_neg;
int_ops::WallaceMultiplier m_mul;
int_ops::EqualDepth m_equal;
int_ops::LowerCompDepth m_lower;
};
}
In the circuit generation file (e.g., cardio.cxx
), the desired optimization strategy must be chosen by passing the strategy to the CiContext
:
CiContext::set_config(
make_shared<BitTracker>(),
make_shared<IntOpGenDepth>()); <---
We have created a FindCingulata.cmake
file for use in our benchmarking applications that provides the necessary boilerplate to integrate Cingulata into a traditional cmake based project. The system searches for Cingulata in /cingu
and exposes the necessary libraries and include files to allow successful compilation of the program logic file (e.g. cardio.cxx
). Support for generating actual circuits and running them is planned for a future iteration.
Note: FindCingulata.cmake
is not used anymore as we use the generated circuits directly during the benchmarking (instead of re-creating it every time) and as such do not need to link against Cingulata. See commit 6631f06c for the solution with FindCingulata.
- Home
- Compilers & Optimizations
- Libraries
- Benchmark Programs
- Implementation Docs