This repository contains a Rust implementation of an homomorphic encryption scheme.
Homomorphic encryption allows computations to be performed on encrypted data without decrypting it, preserving the privacy of the data. Homomorphic encryption is still a subject of research today, and no system that is both secure and efficient has yet been found.
The crate provides a simple API to define a proper homomorphic implementation of any structure, but also provide an implementation for integers (other std types are a work in progress)
I do not intend to publish the crate on crates.io
.
-
Clone the repository:
git clone https://github.com/mathisbot/homomorph-rust.git
-
Build the project:
cargo build --lib --release
-
(Optional) Run the tests:
cargo test
-
If you want to use this crate as a module for your own crate, please refer to the documentation, where you'll also find examples :
cargo doc
custom_rand
: Allows to implement a fallback method togetrandom
on unsupported targets
The crates supports no_std
environments.
As Vec
is used a lot, the crate relies on an external alloc
crate.
As each bit ciphered takes up a lot of space, storing ciphered objects
on the stack wouldn't be possible (at least on low end machines).
This is why the heap is needed here.
You may also need a source of randomness.
The crate uses getrandom
in the backend, so please refer to their documentation for a list of supported targets
as well as instructions on how to implement a custom source for other targets.
Benchmarks were made using a Ryzen 7 7800x3D on u32
s using cargo bench --bench u32
.
Parameters used for this benchmark were :
d
= 128dp
= 128delta
= 1tau
= 128
Operation | Average time |
---|---|
Encryption | 76.0 µs |
Decryption | 12.5 µs |
Add | 950 µs |
Dec. after add | 1.03 ms |
Mul | Uncomfortably long |
It is still more efficient to decrypt, operate and then re-encrypt the data. This limits the use of the system to applications where security is paramount, and takes precedence over speed.
It's worth remembering that the system is inherently slow, as each bit is ciphered as a polynomial whose degree is at least
The properties of homomorphic encryption make it a great candidate for computations in unsafe environments.
It's best to encrypt/decrypt locally, and only perform homomorphic operations on the external environment.
If this is not possible, it may be worth taking certain precautions:
- Zeroize unciphered data using
zeroize
(it is done forSecretKey
) - Protect memory by using
mimalloc
with itssecure
feature enabled (mimalloc
will generally improve performance).
For more information about what homomorphic encryption schemes are, see Wikipedia.
The system is defined by 4 parameters :
A secret key
A public key
- Generate two random polynomials :
$Q_i \in \mathbb{Z}/2\mathbb{Z}_{d'}[X]$ $R_i \in \mathbb{Z}/2\mathbb{Z}_{\delta}[X]$
-
$T_i$ is the sum$SQ_i + XR_i$
So that
Encryption of bit
- Generate
$\mathcal{U} \in \mathcal{P}([1,\tau])$ - Encrypted polynomial is
$C = (\sum_{i\in\mathcal{U}} T_i) + x$
Decryption of a cipher
- Compute
$R$ the quotient of the euclidean division of$C$ by$S$ -
$x$ is$R$ evaluated at$0$
This is why
Encryption schemes security is complex to quantify. However, we can be sure of a system's insecurity if, for example, it is possible to: retrieve the private key from one or more public keys; retrieve the plaintext of an encrypted message without having the private key, ...
In our case, let's look at the potential loopholes.
Retrieving the private key with only the public key (or a set of them) is equivalent to solving the problem RLWE (thus the shape of our public key). This problem has been proved as computationally infisible, which means that no current machine, and no machine that may soon be developed, can solve it in a time that is humanly conceivable. Great!
Often, bruteforce or the use of order relations compatible with the encryption function can be used to break the encryption.
In our case, the parameter
In view of the preceding discussions, it would seem advisable to choose a
This system is partially homomorphic, which means that it is not homomorphic with every operations.
However, one can prove that it is homomorphic with every
boolean function
of degree less or equal than
Our system targets bits. If we want to deal with interesting data, such as integers, floats, strings, ... we need to extend the system. We can easily take advantage of the intuitive binary representation of objects in computers.
Let's have a look at how to implement the system for integers.
Proceeding in a similar way to a processor, we can reduce the addition of integer ciphers (i.e. lists of ciphered bits) to the application of serial logic gates to the bits. It's then easy to find the Boolean function relating to the application of these gates.
One can notice that the AND gate corresponds to multiplying two ciphers, XOR to adding two ciphers, and OR to adding the sum and the product of two ciphers.
By playing with the same adder patterns that are in CPU's ALUs, we can easily recreate a working addition for our ciphers.