Only this pageAll pages
Powered by GitBook
1 of 23

v0.9.0

Loading...

Getting Started

Loading...

Loading...

Tutorials

Loading...

Loading...

How To

Loading...

Loading...

Loading...

Loading...

Use CKKS

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

API References

Loading...

etc

Loading...

Multiplication

with re-linearization, rescale

TBD

Encrypt, Decrypt

TBD

Add, Sub

TBD

Multiparty

Multiparty

The Multiparty FHE (MFHE) approach combines the features of a distributed trust model and the privacy-preserving capabilities of homomorphic encryption. In the MFHE, no single participant stores the secret key for decryption. This decryption system ensures that no one can access encrypted data alone, and decryption requires consensus from all participants.

In this tutorial, we will explore the usage of the Multiparty scheme in Liberate.FHE. The structure of this tutorial is as follows:

  1. Required Package Import

  2. Parameter Configuration & Generate CKKS Engine

  3. Generation of keys required for operations

    1. Secret Key

    2. Collective Public Key

    3. Collective Evaluation Key

    4. Collective Rotation Key

  4. Encryption

  5. Homomorphic Operations

  6. Decryption

Required package import

import numpy as np
from liberate import fhe
from liberate.fhe import presets

Import the necessary packages in Liberare.FHE.

Parameter configuration & generate CKKS engine

grade = "silver"
params = presets.params[grade]

engine = fhe.ckks_engine(**params, verbose=True)

Set up the homomorphic parameters and create the CKKS engine.

Generation of keys

Secret key

# set number of parties
num_parties = 5

# generate each party's secret key
sks = [engine.create_secret_key() for _ in range(num_parties)]

Set the number of participants in num_parties and generate the corresponding secret key.

Collective public key

# generate collective public key
pks = [engine.create_public_key(sk=sks[0])]
# share the crs every party
crs = engine.multiparty_public_crs(pk=pks[0])
for sk in sks[1:]:
    pks.append(engine.create_public_key(sk=sk, a=crs))

cpk = engine.multiparty_create_collective_public_key(pks=pks)

To generate a collective public key, several steps are required. First, the Common Reference String (CRS), which is a value that each party needs to share, is generated. Then, each party generates their public key using their own secret key and the shared CRS. After generating the public key for each party, the collective public key is created using these keys and shared with each party.

Collective evaluation key

# generate collective evaluation key
evks_share = [engine.create_key_switching_key(sks[0], sks[0])]
# share the crs every party
crs = engine.generate_rotation_crs(evks_share[0])
# generate each party's evk_share
for sk in sks[1:]:
    evks_share.append(engine.multiparty_create_key_switching_key(sk, sk, a=crs))

# summation evk shares
evk_sum = engine.multiparty_sum_evk_share(evks_share)

# share the evk_sum each party
evk_sum_mult = [engine.multiparty_mult_evk_share_sum(evk_sum, sk) for sk in sks]

# summation evk_sum_mult
cevk = engine.multiparty_sum_evk_share_mult(evk_sum_mult)

All participants will be shared with the CRS value. Then, each party will generate the secret shared evks_share using the shared CRS and their own secret key (sk). The generated evks_share will be used to create evks_sum and shared with all parties. Each participant will use the shared evks_sum and their sk to generate evk_sum_mult, and then share the generated evk_sum_mult to create the collective evaluation key (cevk) and share it with all participants.

Collective rotation key

# crotk
delta = 5
rotks = [engine.multiparty_create_rotation_key(sk=sks[0], delta=delta)]
crss = engine.generate_rotation_crs(rotks[0])

for sk in sks[1:]:
    rotks.append(engine.multiparty_create_rotation_key(sk=sk, delta=delta, a=crss))
crotk = engine.multiparty_generate_rotation_key(rotks)

Encryption

m0 = engine.example(-1, 1)
m1 = engine.example(-10, 10)

ct0 = engine.encorypt(m0, cpk)
ct1 = engine.encorypt(m1, cpk, level=3)

Encryption follows the same process as general encryption, except that it uses cpk.

Homomorphic operations

ct_mult = engine.mult(ct0, ct1, cevk)
ct_rot = engine.rotate_single(ct1, rotk=crotk)

The Homomorphic Encryption operation is similar to the usual process of Homomorphic Encryption, with the only difference being that it utilizes a collectively generated key by all participants.

Decryption

##### just decrypt
pcts = [engine.multiparty_decrypt_head(ct0, sks[0])]
for sk in sks[1:]:
    pcts.append(engine.multiparty_decrypt_partial(ct0, sk))
m_ = engine.multiparty_decrypt_fusion(pcts, level=ct0.level)

##### decrypt mult
pcts = [engine.multiparty_decrypt_head(ct_mult, sks[0])]
for sk in sks[1:]:
    pcts.append(engine.multiparty_decrypt_partial(ct_mult, sk))
m_ = engine.multiparty_decrypt_fusion(pcts, level=ct_mult.level)
m_mult = m0 * m1

######## decrypt rotate
pcts = [engine.multiparty_decrypt_head(ct_rot, sks[0])]
for sk in sks[1:]:
    pcts.append(engine.multiparty_decrypt_partial(ct_rot, sk))
m_roll = engine.multiparty_decrypt_fusion(pcts, level=ct_rot.level)

The process of decryption consists of three steps. These steps are multiparty_decrypt_head, multiparty_decrypt_partial, and the final step multiparty_decrypt_fusion. One participant uses the head function, while the remaining participants use the partial function. Then, the participant who wants to see the decrypted result executes the fusion function.

The code used in this tutorial is as follows:

import numpy as np
from liberate import fhe
from liberate.fhe import presets

grade = "silver"
params = presets.params[grade]

engine = fhe.ckks_engine(**params, verbose=True)

# set number of parties
num_parties = 5

# generate each party's secret key
sks = [engine.create_secret_key() for _ in range(num_parties)]

###########################################
# generate collective public key
pks = [engine.create_public_key(sk=sks[0])]
# share the crs every party
crs = engine.multiparty_public_crs(pk=pks[0])
for sk in sks[1:]:
    pks.append(engine.create_public_key(sk=sk, a=crs))

cpk = engine.multiparty_create_collective_public_key(pks=pks)

###########################################
# generate collective evaluation key
evks_share = [engine.create_key_switching_key(sks[0], sks[0])]
# share the crs every party
crs = engine.generate_rotation_crs(evks_share[0])
# generate each party's evk_share
for sk in sks[1:]:
    evks_share.append(engine.multiparty_create_key_switching_key(sk, sk, a=crs))

# summation evk shares
evk_sum = engine.multiparty_sum_evk_share(evks_share)

# share the evk_sum each party
evk_sum_mult = [engine.multiparty_mult_evk_share_sum(evk_sum, sk) for sk in sks]

# summation evk_sum_mult
cevk = engine.multiparty_sum_evk_share_mult(evk_sum_mult)

###########################################
# crotk
delta = 5
rotks = [engine.multiparty_create_rotation_key(sk=sks[0], delta=delta)]
crss = engine.generate_rotation_crs(rotks[0])

for sk in sks[1:]:
    rotks.append(engine.multiparty_create_rotation_key(sk=sk, delta=delta, a=crss))
crotk = engine.multiparty_generate_rotation_key(rotks)

###########################################
# cgalk
galks = [engine.create_galois_key(sks[0])]
crss = engine.generate_galois_crs(galks[0])
for sk in sks[1:]:
    galks.append(engine.multiparty_create_galois_key(sk=sk, a=crss))

cgalk = engine.multiparty_generate_galois_key(galks)

##########
m0 = engine.example(-1, 1)
m1 = engine.example(-1, 1)
ct0 = engine.encorypt(m0, cpk)
ct1 = engine.encorypt(m1, cpk, level=3)

####
ct_mult = engine.mult(ct0, ct1, cevk)
ct_rot = engine.rotate_single(ct1, rotk=crotk)

#####
pcts = [engine.multiparty_decrypt_head(ct0, sks[0])]
for sk in sks[1:]:
    pcts.append(engine.multiparty_decrypt_partial(ct0, sk))
m_ = engine.multiparty_decrypt_fusion(pcts, level=ct0.level)

#####
pcts = [engine.multiparty_decrypt_head(ct_mult, sks[0])]
for sk in sks[1:]:
    pcts.append(engine.multiparty_decrypt_partial(ct_mult, sk))
m_ = engine.multiparty_decrypt_fusion(pcts, level=ct_mult.level)
m_mult = m0 * m1

########
pcts = [engine.multiparty_decrypt_head(ct_rot, sks[0])]
for sk in sks[1:]:
    pcts.append(engine.multiparty_decrypt_partial(ct_rot, sk))
m_roll = engine.multiparty_decrypt_fusion(pcts, level=ct_rot.level)

m_roll = np.roll(m1, delta)

print(f">> result decrypt : {np.abs(m_ - m0).max()}")
print(f">> result mult    : {np.abs(m_ - (m_mult)).max()}")
print(f">> result rot     : {np.abs(m_roll - (m_roll)).max()}")

Quick Start

Quick Start

The following example shows the high level APIs of Liberate.FHE which can be used quickly and easily.

1. Import packages for liberate.fhe

2. Configure security parameters & generate CKKS engine

To use homomorphic encryption, you need to configure security parameters according to your requirements. These parameters should be decided based on several factors such as the number of data elements, the number of multiplications, and the desired precision. The presets provide parameter options of different levels for convenience. You can also modify the parameters if needed.

After setting the parameters, you can generate the engine by providing the parameters and any additional options you need.

3. Create keys

Homomorphic encryption uses various types of keys for encryption, decryption and homomorphic operations (a.k.a. homomorphic evaluations). The most basic keys are a Secret Key sk and a Public Key pk. The sk is a private key used for decryption and should not be exposed to external entities. The pk is a key used for encryption and is shared with external users. In addition, an Evaluation Key evk, also known as a Re-linearization Key, is generated for multiplication operations.

4. Encode and encrypt data

In CKKS, data is encrypted through the Encode -> Encrypt process.

The liberate.fhe library provides a shortcut function called encorypt for convenience, which combines the encode and encrypt steps.

5. Perform homomorphic operations with encrypted data

To perform operations using encrypted data, you can utilize the homomorphic operation functions provided by the liberate.FHE library.

6. Decrypt and decode

After performing the homomorphic operations, you can decrypt the ciphertext ct and decode it to obtain the original data.

The liberate.FHE library provides shortcut functions called decrode for convenience, which combines the decrypt and decode steps.


Installation

Liberate.FHE Installation Guide

Liberate.FHE natively supports python programming language. To install and use Liberate.FHE, please follow the steps below:

Install Liberate.FHE

Clone Liberate.FHE github repository

Install dependencies

Use poetry to install the project dependencies. Open a terminal and run the command poetry install. This will automatically install all the required packages for the Liberate.FHE.

Run CUDA compile script

To compile CUDA by running the setup.py file. In the terminal, run the command poetry run python setup.py install. This command will compile CUDA files.

Build a python package

Build the project by running the command poetry build in the terminal. This will create a distributable format of the package.

Install Liberate.FHE library

Install the Liberate.FHE by running the command poetry run python -m pip install . in the terminal. This will install the Liberate.FHE library into your system.


System Requirements

Configure

To install Liberate.FHE, there are a few prerequisites that need to be met. First, you will need an Nvidia graphics card. Additionally, CUDA and Python must also be installed. Only after meeting these requirements can you proceed with the installation of Liberate.FHE.

Homomorphic Statistics

The purpose of this tutorial is to calculate the mean and variance for all the data using approximately 10 million encrypted records. This will enable more accurate and reliable data analysis. Additionally, by using Liberate.FHE as in this tutorial, you can confirm that it is possible to implement Homomorphic operations with very simple code.

In this tutorial, you can find the complete codes for mean and variance as follows:

Setup

Import necessary packages

Import the package you want to use. fhe is an essential package from the Liberate Library for homomorphic encryption. fhe.presets is a package that provides convenience for importing pre-configured homomorphic parameters.

Generate CKKS engine

Generate encryption keys

We will generate the keys to be used in the computation. The keys used in this tutorial are as follows.

  • sk: The secret key is a confidential piece of information that is kept secret by the data owner or a trusted entity. The secret key is typically used for decrypting data. In homomorphic encryption, it is used to decrypt the results of computations performed on encrypted data. The secrecy of the secret key is crucial for the security of the encrypted data. If compromised, an attacker could decrypt the results and potentially gain access to sensitive information.

  • pk : The public key is a component of the homomorphic encryption system that is openly shared. The public key is used for encrypting data. While it can be used to encrypt data, it cannot be used to decrypt the results of computations performed on that data. Unlike the secret key, the public key is shared openly and does not need to be protected in the same way. Its security is not compromised even if it is known to potential adversaries.

  • evk : The evaluation key is a cryptographic component used in homomorphic encryption schemes that support relinearization. It is used during the relinearization process to transform ciphertexts, typically after multiplication operations. While not as sensitive as the secret key, the relinearization key may still require protection, and its use is generally limited to trusted entities.

  • gk : The Galois key is a cryptographic component used in certain homomorphic encryption schemes to support specific operations, particularly rotations and linear transformations in the Galois field. The Galois key enables the efficient execution of mathematical operations on encrypted data while preserving the confidentiality of the underlying information. While the Galois key is an important component, it is not as sensitive as the secret key in homomorphic encryption.

Generate data and encrypt data

In this tutorial, we will calculate the average and variance of approximately 10 million data points. These 10 million data points will be generated assuming they represent the ages of the population in a specific city. Therefore, we assume the age ranges from 0 years old to 99 years old and generate them using a random function. Additionally, we will trim the data slightly to match the number of security parameters we have set, rather than exactly 10 million. Since we have set logN to 16, the number of available slots is 32,768. Hence, the number of ciphertexts we will use is approximately round(10,000,000/32,768) = 305. We will then proceed with encoding and encrypting using the encrypt function. With the data prepared, we can now begin by calculating the mean (average).

Mean

The code for calculating mean in Liberate.FHE is as follows.

  1. Calculate the total of all the values in the dataset.

Therefore, we use the add function to calculate the sum of all ciphertexts.

To calculate the average of all encrypted texts, use the mean provided by Liberate.FHE.

Variance

Now, we can use the average value obtained in this way to calculate the variance.

The formula to calculate dispersion is as follows.

  1. Calculate the average of the data : Utilize the previously computed average.

  2. Subtract the average from each data point.

  1. Calculate the sum of all squared deviations.

  2. Divide the sum of squared deviation by the number of values.

To calculate the difference between each data point and the previously calculated average, we can use the sub function from Liberate.FHE. Then, we can calculate the square of the differences using the square function. By summing up the squared differences and using the mean function, we can obtain the variance.

Result verify

Welcome to Liberate.FHE!

version test

Liberate.FHE is an open-source Fully Homomorphic Encryption (FHE) library for bridging the gap between theory and practice with a focus on performance and accuracy.

Liberate.FHE is designed to be user-friendly while delivering robust performance, high accuracy, and a comprehensive suite of convenient APIs for developing real-world privacy-preserving applications.

Liberate.FHE is a pure Python and CUDA implementation of FHE. So, Liberate.FHE supports multi-GPU operations natively.

The main idea behind the design decisions is that non-cryptographers can use the library; it should be easily hackable and integrated with more extensive software frameworks.

Additionally, several design decisions were made to maximize the usability of the developed software:

  • Make the number of dependencies minimal.

  • Make the library easy to understand and modify.

  • Set the usage of multiple GPUs as the default.

  • Make the resulting library easily integrated with the pre-existing software, especially Artificial Intelligence (AI) related ones.

Key Features

  • RNS-CKKS scheme is supported.

  • Python is natively supported.

  • Multiple GPU acceleration is supported.

  • Multiparty FHE is supported.

Features in Delivery

  • CKKS bootstrapping

  • Liberate.FHE CPU version

  • IND-CPA-D Security for CKKS

Source Codes

License

Liberate.FHE is available under the BSD 3-Clause Clear license. If you have any questions, please contact us at contact@desilo.ai.

Support

  • Support forum: TBD

GPU & CUDA

To use Liberate.FHE, a GPU is required. This document explains how to set up and install the Nvidia graphics card driver and CUDA. It provides steps to identify and install the GPU, update package lists, install Nvidia drivers, verify the installation, install the CUDA toolkit, set environment variables, and verify the CUDA installation.

Setting graphic card

GPU

Step 1: Identify your GPU

Before you begin, it's essential to identify the Nvidia GPU model in your system. You can use the following commands to check:

If the Nvidia graphics card is not installed or an incompatible version of CUDA is installed, install a new version.

Step 2: Update package lists

Ensure that your package lists are up to date.

Check available graphic cards.

Step 3: Install nvidia driver

Automatically install the recommended drivers.

Alternatively, manually install the desired version.

Don't forget to restart the server after installing the graphic card driver.

Step 4: Verify installation

Please ensure that the graphics card is in working order.


CUDA

Step 1: Verify your GPU

Step 2: Install the nvidia GPU drivers

Please refer to the instructions provided above.

Step 3: Download CUDA toolkit

Step 4: Install the CUDA toolkit

Assuming you've downloaded the toolkit:

Step 5: Set environment variables

Add CUDA to your PATH and update LD_LIBRARY_PATH. You can do this by adding the following lines to your ~/.bashrc or ~/.zshrc file:

Remember to provide the source for your updated profile:

Step 6: Verify installation

Please verify if CUDA has been installed correctly by executing the following command:

This should display the CUDA compiler version.


Python

Installing python

This document offers comprehensive guidelines for installing Python on Ubuntu. It discusses multiple installation methods, such as utilizing APT, Anaconda, or pyenv for Python version management. The document also provides detailed instructions for installing Anaconda, setting up a virtual environment with Anaconda, and installing and configuring pyenv for Python version and virtual environment management.

Installing python using apt

If Python ^3.10 is not already installed, you have the option to use APT to install it on your system. To do so, please follow these steps:

To update the system repository list, use the following command:sudo apt install python3

  1. To set up Python, install the required dependencies using the following command:sudo apt

  2. Install the latest version of Python with the command:


(Recommended) Using Anaconda or pyenv

Instead of directly installing Python on your system, it is recommended to use Python version management tools like Anaconda or pyenv.

Installing python using anaconda

If you are using Linux and want to use GUI packages, you will need to install additional dependencies for Qt.

To install the latest version of Anaconda, follow these steps:

  1. Download the latest version of Anaconda from the official website:shasum -a 256

  2. (Recommended) Ensure the integrity of the installer's data by verifying it with SHA-256. For additional details on hash verification, please refer to the documentation on cryptographic hash validation.

  3. Install Anaconda by running the downloaded installer :

  4. Update all packages in Anaconda

  5. Set up a Python Virtual Environment using Anaconda :

  6. Activate the virtual environment :


Installing python using pyenv

To configure pyenv, you can simply follow these steps :

  1. Download pyenv using the following command:

  2. Update pyenv to the latest version :

  3. To ensure proper configuration, please add the necessary settings to your shell startup file (~/.bashrc, ~/.zshrc, etc.), based on your shell environment.

    • For Bash :

    • For Zsh :

  4. To install the Python version you want, you can use pyenv. First check the available Python versions using the command pyenv install --list. Then, install a specific version by running the appropriate command.

  5. To start, create a Python virtual environment and activate it :

Liberate.FHE provides a nnn of nnn multiparty FHE algorithm where nnn is the number of participants. nnn of nnn means that all participants who were involved in the key generation process must also be involved in the decryption process. Liberate.FHE's MFHE is designed to follow the technique introduced in and papers. This approach shares similarities with the single-key method in terms of homomorphic operations but differs in the manner keys are generated. All participants have their own secret keys, and a collective public key is generated by the participants' public keys. Therefore the collective encryption key should be generated again whenever there is a change in the member of participants. However, the computation time and the size of a ciphertext are almost identical to that of the single-key encryption. Decryption should be done interactively with the participation of all parties involved. Liberate.FHE offers user-friendly APIs of all the MFHE processes for supporting real-world multiple participants' use cases.

The process of generating the Collective Rotation Key (crotk) is the same as the process of .

To start using homomorphic encryption such as the CKKS scheme with the liberate.fhe package, you need to import the necessary packages. The liberate.fhe package is the essential for using the CKKS scheme. Additionally, pre-configured information can be found in the liberate.fhe package's .

For more detailed information on the parameters used for engine generation, you can refer to the .

Please refer to the for information on generating different keys.

For more detailed information on the homomorphic operations used, you can refer to the .

This section is a brief guide on how to start programming homomorphic encryption applications using the liberate.fhe package with the CKKS scheme. It includes steps such as package import, engine configuration and creation, homomorphic key generation, data encoding and encryption, performing operations on encrypted data, decryption and decoding. For more detailed information, please refer to the .

and setup

Liberate.FHE runs on single or multiple GPUs. Running Liberate.FHE on GPUs requires installing . Additionally, you need to install that matches the version of you intend to use. Theses settings are necessary for GPU-based operations.

(including Virtual Environment)

To build Liberate.FHE, you need to install , a dependency manager and packaging tool for Python. It is recommended to set up a virtual environment to manage the project in an isolated environment.

Clone the of Liberate.FHE to obtain the latest source codes.

Operating System : Liberate.FHE is compatible with .

Python : Liberate.FHE requires version 3.10 or above. You can download and install Python from the official website. And we recommend using the python virtual environment.

PyTorch : Liberate.FHE uses the package. When you install Liberate.FHE, it automatically installs PyTorch.

CUDA : If you want to utilize the GPU, install . Ensure that you choose a version of CUDA that is compatible with PyTorch, and install it accordingly.

Detailed steps to install Liberate.FHE can be found on . After downloading Liberate.FHE from GitHub, it needs to be installed as a Python package

To create the CKKS engine, we utilize pre-configured parameters. For this tutorial, the engine has been created using the . In the case of gold, the value of logN is 16, there are 4 special primes, and a total of 34 levels are available.3.

To calculate the average, divide the sum by the number of values .

Anyone can easily start to use Liberate.FHE by following after installing the library via .

If you encounter any issues during the installation or need further customization, you can refer to the official or seek help from the Ubuntu community forums.

Make sure your GPU is CUDA-compatible. You can find a list of CUDA-enabled GPUs on the .

Visit the Nvidia download page.

Before installing on Ubuntu, it is important to check if Python is already installed on your system. You can do this by running the following command:

is a software package that includes Python and R programming languages. It is widely used in scientific computing for various fields like data science, machine learning, and large-scale data processing.

For more information on how to use Anaconda and its features, please refer to the .

is a tool that enables you to conveniently handle multiple Python versions and virtual environments.

To learn more about using pyenv and its features, please refer to the .

cpk
example.py
from liberate import fhe
from liberate.fhe import presets

# Generate CKKS engine with preset parameters
grade = "silver"  # logN=15
params = presets.params[grade]

engine = fhe.ckks_engine(**params, verbose=True)

# Generate Keys
sk = engine.create_secret_key()
pk = engine.create_public_key(sk)
evk = engine.create_evk(sk)

# Generate test data
m0 = engine.example(-1, 1)
m1 = engine.example(-10, 10)

# encode & encrypt data
ct0 = engine.encorypt(m0, pk)
ct1 = engine.encorypt(m1, pk, level=5)

# (a + b) * b - a
result = (m0 + m1) * m1 - m0
ct_add = engine.add(ct0, ct1)  # auto leveling
ct_mult = engine.mult(ct1, ct_add, evk)
ct_result = engine.sub(ct_mult, ct0)

# decrypt & decode data
result_decrypted = engine.decrode(ct_result, sk)
from liberate import fhe
from liberate.fhe import presets
# use presets
grade = "silver"
params = presets.params[grade]

# simple modify
params["scale_bits"] = 39
# Generate the engine
engine = fhe.ckks_engine(**presets,
                         security_bits=128, # Additional options
)
# Create the secret key
sk = engine.create_secret_key()

# Create the public key using the secret key
pk = engine.create_public_key(sk=sk)

# Create the evaluation key using the secret key
evk = engine.create_evk(sk=sk)
# Generate test data
message_a = engine.example(amin=-1., amax=1.)
message_b = engine.example(amin=-1., amax=1.)

# Encode the data
pt_a = engine.encode(m=message_a, level=0)
pt_b = engine.encode(m=message_b, level=0)

# Encrypt the encoded data
ct_a = engine.encrypt(pt=pt_a, pk=pk, level=0)
ct_b = engine.encrypt(pt=pt_b, pk=pk, level=0)
# Generate test data
message_a = engine.example(amin=-1., amax=1.)
message_b = engine.example(amin=-1., amax=1.)

# 
ct_a = engine.encorypt(m=message_a, pk=pk, level=0)
ct_b = engine.encorypt(m=message_b, pk=pk, level=0)
# Perform (a * b) + a
ct_mult = engine.mult(a=ct_a, b=ct_b, evk=evk)
ct_result = engine.add(a=ct_mult, b=ct_a)
# Decrypt the ciphertext
pt_result = engine.decrypt(ct=ct_mult, sk=sk)

# Decode the decrypted result to obtain the original data
message_result = engine.decode(m=pt_result, level=ct_mult.level)
message_result = engine.decrode(ct=ct_result, sk=sk)
git https://github.com/Desilo/liberate-fhe.git
poetry install
python setup.py install
# poetry run python setup.py install
poetry build
pip install .
# poetry run python -m pip install .
var.py
import numpy as np
from liberate import fhe
from liberate.fhe import presets

# generate engine
params = presets.params["gold"]
engine = fhe.ckks_engine(verbose=True, **params)

# generate Keys
sk = engine.create_secret_key()
pk = engine.create_public_key(sk)
evk = engine.create_evk(sk)
gk = engine.create_galois_key(sk)

# set num of data
target_N = 10 ** 7  # 10,000,000
required_num_ct = round(target_N / engine.num_slots)

# generate Data
cts = []
ms = []
age_range = [0, 100]
for _ in range(required_num_ct):
    m = np.random.randint(age_range[0], age_range[1], engine.num_slots)
    ct = engine.encorypt(m, pk, level=engine.num_levels - 8)
    # ct = engine.encorypt(m, pk, level=0)
    ms.append(m)
    cts.append(ct)

#################################
########    calc mean    ########
#################################
result = engine.add(cts[0], cts[1])

for ct in cts[2:]:
    result = engine.add(result, ct)
ct_mean = engine.mean(result, gk, alpha=required_num_ct)

calculated_mean = engine.decrode(ct_mean, sk)
target_mean = np.array(ms).mean()

print("=====" * 15)
print(f">>\tcalc mean\t:\t{calculated_mean[0].real:20.13f}")
print(f">>\ttarget mean\t:\t{target_mean:20.13f}")
print(f">>\tabs max error\t:\t{np.abs(calculated_mean.real - target_mean).max():20.13e}")

################################
########    calc var    ########
################################
devs = []
for ct in cts:
    dev = engine.sub(ct, ct_mean)

    dev = engine.square(dev, evk)
    devs.append(dev)

result = engine.cc_add(devs[0], devs[1])

for dev in devs[2:]:
    result = engine.cc_add(dev, result)


ct_var = engine.mean(result, gk, alpha=required_num_ct)

################################
####    decrypt & decode    ####
################################
calculated_var = engine.decrode(ct_var, sk).mean()
target_var = np.array(ms).real.var() + np.array(ms).imag.var() * 1j

print("=====" * 15)
print(f">>\tcalc var\t: {calculated_var.real:20.13e}")
print(f">>\ttarget var\t: {target_var.real:20.13e}")
print(f">>\terror\t\t: {(calculated_var - target_var).real:20.13e}")
import numpy as np
from liberate import fhe
from liberate.fhe import presets
# generate engine
params = presets.params["gold"]
engine = fhe.ckks_engine(verbose=True, **params)
# generate Keys
sk = engine.create_secret_key()
pk = engine.create_public_key(sk)
evk = engine.create_evk(sk)
gk = engine.create_galois_key(sk)
# set num of data
target_N = 10 ** 7  # 10,000,000
required_num_ct = round(target_N / engine.num_slots)

# generate Data
cts = []
ms = []
age_range = [0, 99]
for _ in range(required_num_ct):
    m = np.random.randint(age_range[0], age_range[1], engine.num_slots)
    ct = engine.encorypt(m, pk, level=0)
    # ct = engine.encorypt(m, pk, level=engine.num_levels - 8)
    ms.append(m)
    cts.append(ct)
∑i=0n−1xi=x0+x1+⋯+xn−1\sum_{i=0}^{n-1}x_{i} = x_{0}+x_{1}+\cdots+x_{n-1}i=0∑n−1​xi​=x0​+x1​+⋯+xn−1​
nnn
E(X)=∑i=0n−1xin\text{E}\left(X\right) = \frac{\sum_{i=0}^{n-1}x_{i}}{n}E(X)=n∑i=0n−1​xi​​
result = engine.add(cts[0], cts[1])
for ct in cts[2:]:
    result = engine.add(result, ct)
ct_mean = engine.mean(result, gk, alpha=required_num_ct)
deviation=(xi−E(X))\text{deviation} = \left(x_{i}-\text{E}\left(X\right)\right)deviation=(xi​−E(X))
Var(X)=∑i=0n−1(xi−E(X))2n\text{Var}\left(X\right) = \frac{\sum_{i=0}^{n-1}\left(x_{i}-\text{E}\left(X\right)\right)^{2}}{n}Var(X)=n∑i=0n−1​(xi​−E(X))2​
devs = []
for ct in cts:
    dev = engine.sub(ct, ct_mean)
    dev = engine.square(dev, evk)
    devs.append(dev)

result = engine.cc_add(devs[0], devs[1])
for dev in devs[2:]:
    result = engine.cc_add(dev, result)

ct_var = engine.mean(result, gk, alpha=required_num_ct)
print("=====" * 15)
print(f">>\tcalc mean\t:\t{calculated_mean[0].real:20.13f}")
print(f">>\ttarget mean\t:\t{target_mean:20.13f}")
print(f">>\tabs max error\t:\t{np.abs(calculated_mean.real - target_mean).max():20.13e}")
>>	calc mean	:	    49.4955522744958
>>	target mean	:	    49.4953527231685
>>	abs max error	:	 1.9955132722060e-04
print("=====" * 15)
print(f">>\tcalc var\t: {calculated_var.real:20.13e}")
print(f">>\ttarget var\t: {target_var.real:20.13e}")
print(f">>\terror\t\t: {(calculated_var - target_var).real:20.13e}")
>>	calc var	:  8.3310567454940e+02
>>	target var	:  8.3310766262893e+02
>>	error		: -1.9880795293830e-03
lshw -numeric -C display
lspci | grep -i nvidia
sudo apt update
ubuntu-drivers devices
sudo ubuntu-drivers autoinstall
sudo apt install nvidia-driver-535-server
nvidia-smi
# ex)cuda 12.1.0
wget https://developer.download.nvidia.com/compute/cuda/12.1.0/local_installers/cuda_12.1.0_530.30.02_linux.run
sudo sh cuda_12.1.0_530.30.02_linux.run
export PATH=/usr/local/cuda-12.1/bin${PATH:+:${PATH}}
export LD_LIBRARY_PATH=/usr/local/cuda-12.1/lib64${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}
source ~/.bashrc
nvcc --version
python3 --version
sudo apt update
sudo apt install build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev curl libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev
sudo apt install python3
python3 --version
apt-get install libgl1-mesa-glx libegl1-mesa libxrandr2 libxrandr2 libxss1 libxcursor1 libxcomposite1 libasound2 libxi6 libxtst6
apt-get install libgl1-mesa-glx libegl1-mesa libxrandr2 libxrandr2 libxss1 libxcursor1 libxcomposite1 libasound2 libxi6 libxtst6
shasum -a 256 /PATH/ANACONDA
# Replace /PATH/FILENAME with your installation's path and filename.
bash ~/PATH/TO/ANACONDA
# bash ./Anaconda3-2023.09-0-Linux-x86_64.sh
conda update --all
conda create -n <Environment Name> python=<Version>
# e.g., conda create -n Liberate python=3.11
conda activate <Environment Name>
# e.g., conda activate Liberate
curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash
pyenv update
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc
echo 'command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(pyenv init -)"' >> ~/.bashrc
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
echo '[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc
echo 'eval "$(pyenv init -)"' >> ~/.zshrc
pyenv install --list
pyenv install <PYTHON VERSION>
# ex) pyenv install 3.11.6
pyenv virtualenv <PYTHON VERSION> <VIRTULAENV NAME>
# ex) pyenv virtualenv 3.11.6 liberate
pyenv activate <VIRTUALENV NAME>
# ex) pyenv activate liberate
this
this

GPU & CPU

TBD

Serialize and Deserialize

TBD

Liberate.FHE documentation
nvidia-driver
CUDA
PyTorch
Python setup
poetry
github repository
Ubuntu
Python
pyTorch
CUDA
How to Install
Quick Start
Installation
https://github.com/Desilo/liberate-fhe
Nvidia documentation
Nvidia website
CUDA Toolkit
Python
Anaconda
official documentation
Pyenv
official documentation
Import packages for liberate.FHE
Configure security parameters & generate CKKS engine
Create keys
Encode and encrypt
Perform homomorphic operations with encrypted data
Decrypt and decode
GPU
CUDA

Liberate with WASM

TBD

License

BSD 3-Clause Clear License

Redistribution and use in source and binary forms, with or without modification, are permitted (subject to the limitations in the disclaimer below) provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

  3. Neither the name of DESILO Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE DESILO INC. AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE DESILO INC. OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Docs

Liberate.FHE API Documentation

This comprehensive document provides in-depth information on the usage and functions of the groundbreaking Liberate.FHE library for Homomorphic Encryption. It covers various essential aspects, starting with a detailed explanation of the context and engine generation, which forms the foundation for the library's powerful capabilities. It then delves into the key generation process, ensuring the secure creation of encryption keys that are vital for protecting sensitive data.

Moreover, this document elaborates on the encoding and decoding mechanisms employed by the Liberate.FHE library, enabling seamless transformation of data into encrypted form and vice versa. It also explores the encryption and decryption processes, shedding light on the secure procedures employed to safeguard information while allowing computations to be performed on encrypted data.

In addition to these core functionalities, the document provides insights into the diverse arithmetic functions available within the Liberate.FHE library. These functions empower users to perform mathematical operations on encrypted data, opening up a world of possibilities for secure computations. Furthermore, it highlights support functions that enhance the overall usability and efficiency of the library, ensuring smooth integration and seamless operation.

Last but not least, this document showcases the utility functions offered by the Liberate.FHE library, including the ability to save, load, and print data structures. These functions provide convenient ways to manage and manipulate encrypted data, enabling users to easily store, retrieve, and analyze information as needed.

Overall, this comprehensive document serves as an indispensable guide for users seeking to harness the full potential of the Liberate.FHE library for Homomorphic Encryption. It covers a wide range of topics, ensuring a thorough understanding of the library's capabilities and empowering users to leverage its advanced features for secure and efficient computations.

Before Moving on...

This documentation only covers the high-level APIs of the library. That is, only the publicly exposed functions are explained. There are numerous internal functions that compose the library; however, those are not documented herein.

Composition of This Document

The function API is broken down into 7 parts:

  1. Context generation

  2. Key generation

  3. The basic blocks of encoding/decoding and encrypting/decrypting

  4. Arithmetic functions

  5. Support functions

  6. Runtime reflection

  7. Utility functions such as save, load, export, import, upload (to GPUs), and download (from GPUs)

General Concept and Particular Features of the Library

Liberate.FHE differentiates itself from other packages in that

Arithmetic-Wise Differences

  1. All intermediate calculations are explicitly executed as integers. No float or double conversions occur during computation.

  2. All intermediate calculations and modular reductions are exact, as they are done using the Montgomery Reduction technique.

  3. Texts, including Cipher, Key, and Plain, are stored in GPUs in split form. In particular, the split is alongside the RNS channels, with the exception of the special primes. Special primes are repeated (copied) onto all the GPUs for faster execution of key switching. For details of the partitioning scheme, refer to rns_partition.py under the ntt folder.

Algorithm-Wise Differences

  1. Rescaling is done before the multiplication, not after the multiplication.

  2. The input message is pre-permuted before encoding. This is done to achieve a cyclic-rotation effect by permuting the coefficients. Similarly, the decoded message is post-permuted to restore the intended form..

  3. There are 2 proprietary formulas used for rescaling and key switching. The effect of using these methods results in algorithmic exactness.

  4. The deviation error, relative to the size of the text, that arises from rescaling is explicitly corrected during the decryption stage. This correction significantly enhances the accuracy of homomorphic operations.

Context Generation

There are 2 stages of the context generation.

The first is the CKKS context generation. The generated CKKS context includes basic information about the polynomial length, primes, and etc. The generated context is also saved in the cache folder for faster access at later times if a context with the same configuration is requested.

By default, scale primes with the bit lengths of from 20 to 59 are generated. However, usage of primes under 30 bits are not recommended for accuracy reasons as density of primes in the bit range are sparse, hampering downed the quality of the primes distribution. Also, note that the context support 2 precision types, that are 32 bits and 64 bits integers. Although, it is highly unlikely the 32 bits precision will be used in real situations, the case is included for completeness.

The grammar of calling the context generate functions is as follows:

where,

  • buffer_bit_length : Specifies the bit length of coefficients. For 64-bit integers, use 62 (default), and for 32-bit integers, use 30.

  • scale_bits : Specifies the scale. The scale will be set to . Note that the bits allocated for the integral part of the message will be buffer_bit_length - scale_bits - 2.

  • num_scales : Specifies the number of utilizable homomorphic levels. If not specified explicitly, the context generator will generate the maximum number of levels available given the logN and the security requirements.

  • num_special_primes : This number corresponds to the factor in hybrid key switching. The specified number of primes will be subtracted from the available levels and then used for key switching.

  • sigma: The standard deviation of the discrete Gaussian sampling, when generating small errors.

  • uniform_ternary_secret: Selects the random algorithm when sampling the secret key. Currently this parameter has no effect, and the engine sticks to the uniform ternary sampling method.

  • cache_folder: The path to the cache folder.

  • security_bits: Specifies the strength of the security in terms of bits.

  • quantum: Specifies the quantum security measures. The value can be either post_quantum or pre_quantum.

  • distribution: Specifies the security model. The security level is measured by sampling and measuring the hardness. This parameter selects the sampling method in the process. Note that this is not the sampling distribution for error generation.

  • read_cache: If set to true, and if the cache for the input parameters exists, the context generator will read in the cache instead of generating a fresh one.

  • save_cache: If set to true, a newly generated context will be saved to a cache file.

  • verbose: If set to true, invoking the generator will print out diagnostic messages.

CKKS Engine Generation

The second is the engine generation. An engine contains numerous pre-calculated caches for calculating the NTT transformation and modular operations. The pre-calculated caches are moved to available GPUs at the generation time so that following calculation won't need to move around the caches. Note that these engine parameters are volatile, that means they are not saved to the disk.

Note that you do not need to generate the ckks_context and hand it over to the engine generator, as it is done internally, and automatically.

At the time of engine generation, you can specify which GPUs to use. This will give you the opportunity to experiment with deployment configurations.

The calling API is identical to that of context generation, such that

Semantics of the parameters are the same as the context generation.

In most usage scenarios, you would only need to specify the logN and all the rest will be automatically generated.

For example,

will generate the most typical CKKS engine for you.

Preset Parameters

We have preset parameters that can deliver the best performance for user convenience. These settings are composed of values that can yield optimal results in logN, num_special_primes, and number of GPUs (devices). We have named these settings bronze, silver, gold, and platinum(you may have heard these names somewhere before. Yes, that's right. Just as you might think). Bronze corresponds to the setting when logN is 14, and the subsequent settings are composed of values 15, 16, and 17 respectively. These settings are provided in the form of Python dictionaries, and users can easily modify them as they wish. By providing these preset values, users can avoid the hassle of manually adjusting parameters to achieve the optimal results.

The arguments provided in presets.params are as follows:

And you can use level 7 in bronze, level 16 in silver, level 34 in gold, and level 72 in platinum.

Key Generations

The Secret Key Generation

The secret keysk is the randomly generated secret key. The Homomorphic Encryption Standard recommends refresh of the random seed occasionally. In case of refresh, issue

and then, generate a secret key.

The Public key Generation

Generating a public keypk requires a sk. Equipped with a sk, you can issue

to obtain a public key.

Note that, public keys generated with the same sk have all different values, as it is the security requirement. Although different in values, all the cipher messages that are generated with the same sk will work equally well under homomorphic operations; cipher texts encrypted under the same sk but different pk can be mixed in homomorphic operation. However, in any operation, their currency level must match.

The Evaluation Key Generation

The Evaluation Keyevk is used for multiplication. It can be generated with

The Rotation key Generations

.

In some occasions, you may want to generate a rotation key for a particular rotation. The most typically expected case is where the rotation is repeated frequently is a computation circuit and you want to accelerate the rotation by directly applying a rotation instead of a successive composition of rotations. In such case, use the following API

. Issuing the above function will generate a rotation key valid for the particular rotation.

The Conjugate Key Generation

The Key Switch Key Generation

You can change the secret key of a cipher text to a different secret key. The key switch key is used for such operation. You can issue the following command to generate the key switch key

, where ksk is the key switch key, sk_from is the secret key used to encrypt the cipher text, and sk_to is the new secret key with which you want to encrypt the cipher text.

The Basic Usage Cycle of Encode/Decode and Encrypt/Decrypt

In most cases, the message goes through a usage cycle as

encode → encrypt → Homomorphic Operations → decrypt → decode

The following subsections explain the API for each stage in the usage cycle, except for the homomorphic operations phase. APIs for the operation phase are enumerated in a separate (following) section.

Encode

, where pt denotes a plain text and m a message and level denotes the homomorphic level you wnat to use. Note that the pt you get is a pre-permuted version of the encoded plain text.

Enecrypt

, where ct denotes the cipher text, pk is a public key and level is homomorphic level you want to use.

Encorypt

, where ct denotes the cipher text, pk is a public key and level is homomorphic level you want to use.

Decrypt

, where sk is a secret key.

Decode

, where sk is a secret key and level is homomorphic level you want to use.

Note that you do not need to post-permute the message m. It is automatically handled for you.

Decrode

, where sk is a secret key.

Arithmetic Functions

There are numerous homomorphic arithmetic functions supported. The following subsections explain each of them.

Add / Subtract

Note that a and b can both be cipher texts, or can be plain texts (or a messages). If one of the operands is message, the message will be automatically encoded to match the required format of the plain text.

Also that a and b can both be cipher text or can be plain texts(or messages). If one of the operands is message, the message will be automatically encoded to match the required format of the plain text.

If a and b are at different levels, the engine will do a proper leveling on one of the inputs and calculate the results. The resultant will have the highest level of the two.

Likewise, for subtraction use

.

A word of warning here.

Do NOT attempt to add or subtract cipher texts directly. Use the API.

Since the cipher texts are, by implementation, PyTorch tensors, you may be attempted to add or subtract them directly by using a + b of a - b. Don't. Although, the numbers contained in the tensors are

  1. Coefficients of the Number Theoretic Transformation (NTT) coefficients.

  2. The Montgomery form numbers.

The context engine will do proper computations for you, and hence do not attempt to do it yourself unless you are undeniably certain about what you're doing.

Multiplication

. Composition and permittance of the input parameters are the same as the add/substration. However, one caveat still persists: Division is not provided as an API function.

Since division is inherently iterative, meaning it consumes multiple levels, its API is not provided in the engine API. You will have to devise your own function using the Newton-Rhapson or the Goldschmidt algorithm.

If a and b are at different levels, the engine will do a proper leveling on one of the inputs and calculate the results. The resultant will have the highest level of the two.

Square

Rotate

Cyclically rotate a cipher text using

, where ct is the cipher text, ksk is the galk, shift is the shift distance of the rotation. And if you use the return_circuit option, you can check wich index you have moved to. Signedness of the shift determines the direction of the shift. A positive shift denotes shifting right, and a negative the opposite direction of left.

In case you generated a key switching key that can accommodate one rotation, you can also issue

.

Conjugate

You can conjugate a cipher text by simply calling

.

Key Switch

Note that key switching occurs internally when performing multiplication, rotation, and conjugation. For some reason if you want to change the secret key used to encrypt a cipher text, you can do

, where the original secret key and the new secret key are embedded in the key switching key ksk. Note that the ct must already have been encrypted with the secret key that matches the original secret key embedded in the ksk. Otherwise, you will be confronted with a broken cipher text.

Support Functions

If necessary, you can use the following functions to have more control over homomorphic operations. The functions are issued in the calls of homomorphic multiplication, however you can have finer control over how the multiplications are done by calling them individually.

Atomic Operations of Multiplications

Homomorphic multiplication, is in fact a sequential combination of rescale, cc_mult, and relinearize. In building a computation circuit you can apply some optimization techniques at atomic levels to achieve greater performance. The following are such atomic functions that consist a multiplication.

, where a and b are input parameters that can be a cipher text, an encoded plain text, or a message respectively, and d0, d1, and d2 are components of a raw product.

, where ct_mult has d0, d1, and d2 are the results of applying the something manual multiply function, and new_ct is the relinearized cipher text.

, ct_rescaled have plus one levels compared to the ct.

The rationale behind the design decision is that the rescaling is always done at least in pairs. Obviously, in multiplications two cipher texts get involved and since we are rescaling before multiplication the operation necessitates rescaling of the two involving cipher texts.

Level up

A circumstance will occur frequently where you want to operate on two cipher text but their levels are different. The level up function is the rescue for such circumstances. Matching levels of cipher texts is not an easy business as it might look at first glance; Deviation from rescaling kicks in and the deviation must match at both operands together with the RNS channel lengths. The following function will do the job for you and ease up the headache.

The engine will do multiple (if necessasary) levelings to make the level of the new_ct to reach the level_to. If the level of the ct is already higher or equal to the level_to the function will return raising an exception.

Runtime Reflection

You can investigate the status of the cipher text or the engine at runtime, using

The num_level gives you the number of multiplications you can do with a cipher text.

You can investigate the level at which the cipher text is by issuing

. Note that the level goes up from zero until it reaches depletion at the num_level. That means a freshly encrypted cipher texts will always have the level 0.

You can figure out the kind of a text by calling

. The text can be a plain text, a key, or an array of keys (a key switching key). The data_struct_str will give you a self-explanatory description of what kind of the data_struct is.

Utility functions such as save, load, export, import, upload (to GPUs), and download (from GPUs).

Save

You can save all variable with data_struct like cipher text, secret key, public key, key swithching key, rotation key and galois key.

Load

You can load all variable with data_struct like cipher text, secret key, public key, key swithching key, rotation key and galois key and you can use move_to_gpu to select whether to move to GPU(True) or CPU(False).

Clone

You can copy a variable with data_struct using Clone function.

CPU

CUDA

Print Data Struct

TBD

Liberate

Prerequisite Package

GPU

CUDA

Python

Liberate.FHE

Step 1: Clone the github repository

Step 2: Install dependencies

Use poetry to install the project dependencies. Open the terminal and run the command poetry install. This will automatically install all the required packages for the Liberate.FHE.

Step 3: Run CUDA build script

To build CUDA by running the setup.py file. In the terminal, run the command poetry run python setup.py install. This command will build CUDA files.

Step 4: Install Liberate.FHE library

Install the Liberate.FHE by running the command poetry run python -m pip install .in the terminal. This will install the Liberate.FHE library into your system.

Copyright (c) 2023 Inc. All rights reserved.

As a result of Rescale Before Multiplication, intermediate cipher texts are multiplied with the square of the scale factor. That is . To compensate for the squared , cipher texts are rescaled right before decryption as well.

Primes are selected according to a proprietary condition that prevents drift of the rescale error, that is . The square term comes from multiplication because rescale error is always the consequence of binary multiplication.

logN : Specifies the length of the message polynomial. The length of the polynomial is , and the actual length of the message that can be encoded in the polynomial is . The means you can fill the polynomial with complex numbers.

grade
logN
special primes
devices
scale_bits
num slots
num levels

You can find descriptions for each parameter in the .

The galois key galk is a collection of key switch keys that enable the cyclic rotations. For a polynomial of length , all possible rotations can be represented by rotations. The galk contains such keys. The key generation API is

Conjugation is a transformation of a complex number to . The key for such operation can be generated with

A message is an array of complex numbers of which the length .

Also note that, the sace where both a and b are plain texts(messages) is mermitted since triplet of additions may occur and two of the operands are plain texts.

. Most of the aspects are similar to multiplication, but it is a bit faster than using the function because it has been optimized.

Please refer to the respective page for instructions on how to install a .

Please refer to the respective page for instructions on how to install a .

Please refer to the respective page for instructions on how to install a .

Clone the of Liberate.FHE to obtain the latest source code.

DESILO
Δ2\Delta^{2}Δ2
Δ\DeltaΔ
Δ2/q2\Delta^{2}/q^{2}Δ2/q2
generate_ckks_context
from liberate.fhe.context import ckks_context

ctx = ckks_context(buffer_bit_length=62,
                   scale_bits=40,
                   logN=15,
                   num_scales=None,
                   num_special_primes=2,
                   sigma=3.2,
                    uniform_ternary_secret=True,
                   cache_folder='cache/',
                   security_bits=128,
                   quantum='post_quantum',
                   distribution='uniform',
                   read_cache=True,
                   save_cache=True,
                   verbose=False
                  )
2logN2^{\text{logN}}2logN
2logN/2=2logN−12^{\text{logN}}/2 = 2^{\text{logN} - 1}2logN/2=2logN−1
2logN−12^{\text{logN}-1}2logN−1
from liberate import fhe

engine = fhe.ckks_engine(
            buffer_bit_length=62,
            scale_bits=40,
            logN=15,
            num_scales=None,
            num_special_primes=2,
            sigma=3.2,
            uniform_ternary_secret=True,
            cache_folder='cache/',
            security_bits=128,
            quantum='post_quantum',
            distribution='uniform',
            read_cache=True,
            save_cache=True,
            verbose=False
)
from liberate import fhe

engine = fhe.ckks_engine(logN=15)
from liberate import fhe
from liberate.fhe import presets

grade = "silver"
params = presets.params[grade]
params["num_scales"] = 5 # you can modify easily

engine = fhe.ckks_engine(**params)

bronze

14

1

1

40

8192

7

silver

15

2

1

40

16384

16

gold

16

4

Full

40

32768

34

platinium

17

6

Full

40

65536

72

sk = engine.create_secret_key()
engine.rng.refresh()
pk = engine.create_pulic_key(sk=sk)
evk = engine.create_evk(sk=sk)
nnn
NNN
log⁡2(N)\log_{2}\left(N\right)log2​(N)
log⁡2(N)\log_{2}\left(N\right)log2​(N)
galk = engine.create_galois_key(sk=sk)
rotk = engine.create_rotation_key(sk=sk, delta=my_shift)
α+bi\alpha + b\mathbf{i}α+bi
α−bi\alpha -b\mathbf{i}α−bi
conjk = engine.create_conjugation_key(sk=sk)
ksk = engine.create_key_switch_key(sk_from, sk_to)
2logN−12^{\text{logN}-1}2logN−1
pt = engine.encode(m, level=0)
ct = engine.encrypt(pt, pk, level)
ct = engine.encorypt(m, pk, level)
pt = engine.decrypt(ct, sk)
m = engine.decode(pt, level)
m = engine.decrode(ct, sk)
ct_add = engine.add(ct_a, ct_b)
(a+b+c)\left(a+b+c\right)(a+b+c)
ct_sub = engine.sub(a, b)
ct_mult = engine.mult(ct_a, ct_b, evk)
ct_squared = engine.square(ct, evk)
ct_rotated = engine.rotated_galois(ct, galk, shift, return_cirtuit=False)
ct_rotated = engine.rotated_single(ct, rotk, shift)
ct_conj = engine.conjugate(ct, conjk)
ct_new = engine.switch_key(ct, ksk)
d0, d1, d2 = engine.cc_mult(ct_a, ct_b, relin=False).data
ct_relin = engine.relinearlization(ct_mult, evk)
ct_rescaed = engine.rescale(ct, exact_rounding=True)
ct_new = engine.level_up(ct, level_to)
num_levels = engine.num_levels
level = ct.level
data_struct_str = data_struct.kind
engine.save(data_struct, filename=None)
sk = engine.create_secret_key()
engine.save(sk, filename="./sk.pkl")
data_struct = engine.load(filename, move_to_gpu=True)
sk = engine.load(filename="./sk.pkl", move_to_gpu=False)
data_struct_ = engine.clone(data_struct)
data_struct_cpu = engine.cpu(data_struct)
data_struct_gpu = engine.cuda(data_struct)
engine.print_data_structure(text, level)
──┬── cipher text
  ├── tensor at device 1 with shape torch.Size([3, 32768]).
  ├── tensor at device 0 with shape torch.Size([1, 32768]).
  ├── tensor at device 1 with shape torch.Size([3, 32768]).
  └── tensor at device 0 with shape torch.Size([1, 32768]).
presets
Liberate.FHE documentation
Liberate.FHE documentation
Liberate.FHE documentation
gold parameter
git clone https://github.com/Desilo/liberate-fhe.git
poetry install
python setup.py install
# poetry run python setup.py install
pip install .
# poetry run python -m pip install .
Python
Github repository
Liberate.FHE document
mult
GPU
CUDA