Install TenSEAL via pip
mainThe easiest way to install TenSEAL is using pip, which installs the latest packaged version from PyPI.
$ pip install tensealrepository·main·Indexed 21 days ago
https://github.com/openmined/tensealA library for performing homomorphic encryption operations on tensors, built on top of Microsoft SEAL. TenSEAL provides a high-level Python API for efficient encrypted computations, supporting schemes such as BFV for integers and CKKS for real numbers. It enables operations like element-wise arithmetic, dot products, matrix multiplication, and experimental 2D convolutions using im2col. The library includes a TenSEALContext for managing encryption keys and parameters, with support for automatic relinearization, rescaling, and modulus switching.
The easiest way to install TenSEAL is using pip, which installs the latest packaged version from PyPI.
$ pip install tensealTo include TenSEAL as a dependency in a Bazel workspace, add the following to your WORKSPACE file:
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
git_repository(
name = "org_openmined_tenseal",
remote = "https://github.com/OpenMined/TenSEAL",
branch = "master",
init_submodules = True,
)
load("@org_openmined_tenseal//tenseal:preload.bzl", "tenseal_preload")
tenseal_preload()
load("@org_openmined_tenseal//tenseal:deps.bzl", "tenseal_deps")
tenseal_deps()You can use a pre-built Docker image for a ready-to-use environment.
Run the latest release image:
$ docker container run --interactive --tty openmined/tensealNote: Use openmined/tenseal:dev for the image built from the master branch.
Build a custom image:
$ docker build -t tenseal -f docker-images/Dockerfile-py38 .Run your custom image:
$ docker container run -it tensealTo build TenSEAL from source, ensure you have the following requirements based on your platform:
General Steps:
$ git submodule init
$ git submodule updateWindows Specifics:
You must build the SEAL library using Visual Studio first. Use the SEAL.sln file in third_party/SEAL to build the project native\src\SEAL.vcxproj with Configuration=Release and Platform=x64.
Final Installation: After satisfying dependencies, run:
$ pip install .$ git submodule init
$ git submodule update
$ pip install .During iterative training (e.g., Stochastic Gradient Descent), the multiplicative depth of ciphertexts increases with each operation. Once the ciphertext reaches its maximum depth, you can no longer perform multiplications.
Workaround: After each epoch, you must decrypt the model parameters (weights and bias) and re-encrypt them using the context. This resets the ciphertext level, allowing for further training iterations. In a real-world distributed setting, this involves sending the weights back to the secret-key holder for re-encryption.
for epoch in range(EPOCHS):
# Re-encrypt parameters to reset multiplicative depth
eelr.encrypt(ctx_training)
for enc_x, enc_y in zip(enc_x_train, enc_y_train):
enc_out = eelr.forward(enc_x)
eelr.backward(enc_x, enc_out, enc_y)
eelr.update_parameters()
# Finally decrypt to check results
eelr.decrypt()When configuring TenSEAL for CKKS or BFV schemes, you must balance security, performance, and precision using three main parameters:
poly_modulus_degree): Must be a power of 2 (e.g., 1024, 2048, 4096, 8192, 16384, 32768).coeff_mod_bit_sizes): A list of binary sizes used to generate primes.scale = 2 ** precision).TenSEAL automates two critical CKKS operations to manage noise and ciphertext size:
Relinearization: When multiplying two ciphertexts, the resulting ciphertext size grows. Relinearization reduces the size back to 2. This is required for efficient subsequent multiplications. TenSEAL performs this automatically after each encrypted multiplication using the relinearization keys.
Rescaling: Homomorphic multiplications cause the approximation error (noise) to grow exponentially. Rescaling (a form of modulus-switching) scales the message down and switches the ciphertext to a smaller modulus, making the error growth linear instead of exponential. This consumes one prime from the coeff_mod_bit_sizes list. Once all primes are consumed, no further multiplications can be performed.
Standard activation functions like sigmoid cannot be computed directly on encrypted data. Instead, use a low-degree polynomial approximation.
A degree-3 polynomial approximation for the range $[-5, 5]$ is: $$\sigma(x) = 0.5 + 0.197x - 0.004x^3$$
In TenSEAL, you can use the .polyval() method on a ckks_vector to evaluate this polynomial. Using a lower degree is critical to minimize multiplicative depth and allow for smaller encryption parameters.
@staticmethod
def sigmoid(enc_x):
# We use the polynomial approximation of degree 3
# sigmoid(x) = 0.5 + 0.197 * x - 0.004 * x^3
return enc_x.polyval([0.5, 0.197, 0, -0.004])By default, a TenSEALContext is 'private', meaning it holds the secret key and can be used for decryption. You can convert a context to 'public' using .make_context_public(), which drops the secret key. This is useful when you want to send the context to a remote worker to perform computations without giving them the ability to decrypt the results.
Use .is_private() and .is_public() to check the current state of the context.
public_context = ts.context(ts.SCHEME_TYPE.BFV, poly_modulus_degree=4096, plain_modulus=1032193)
# Check status
print("Is private?", public_context.is_private())
# Drop the secret key to make it public
public_context.make_context_public()
print("Is public?", public_context.is_public())In the CKKS scheme, precision is impacted by the relationship between the input data range, the scaling factor, and the coefficient modulus sizes.
When performing operations like addition or multiplication, the available precision (the power of 2 by which you can approximate the result) may decrease. If the input values are too large relative to the chosen parameters, encryption or subsequent operations may fail.
TenSEAL provides an experimental way to perform 2D convolutions on encrypted data using the im2col (Image Block to Column) approach.
To perform a 2D convolution:
ts.im2col_encoding to transform the input image into an encrypted format suitable for convolution. This function returns an encrypted vector and the number of windows produced..conv2d_im2col() method on the resulting encrypted vector. This method takes the kernel (as a list) and the number of windows as arguments.Note on Workflow: For multi-layer convolutions, a client-server communication pattern is required. The server sends the ciphertext to the client, the client decrypts it and applies im2col to the plaintext, then re-encrypts the data to send back to the server for the next layer.
# 1. Encode and encrypt the input image
x_enc, windows_nb = ts.im2col_encoding(context, x, kernel.shape[0], kernel.shape[1], stride)
# 2. Perform the convolution on the ciphertext
y_enc = x_enc.conv2d_im2col(kernel.tolist(), windows_nb)
# 3. Decrypt the result
y_plain = y_enc.decrypt()To perform encrypted convolutions, you must initialize a TenSEAL context using the CKKS scheme. You need to specify the polynomial modulus degree, coefficient modulus bit sizes, and a global scale. Additionally, you must generate Galois keys to enable rotations on ciphertext vectors.
Required configuration steps:
ts.SCHEME_TYPE.CKKS.context.global_scale (e.g., pow(2, 40)).context.generate_galois_keys() to support rotations.import tenseal as ts
# Create TenSEAL context
context = ts.context(
ts.SCHEME_TYPE.CKKS, 8192, coeff_mod_bit_sizes=[60, 40, 40, 60]
)
# Set the scale
context.global_scale = pow(2, 40)
# Generate galois keys in order to do rotation on ciphertext vectors
context.generate_galois_keys()