CiFEr
|
CiFEr (prounounced as cipher) is a cryptographic library offering different state-of-the-art implementations of functional encryption schemes, specifically FE schemes for linear polynomials (e.g. inner products). It is implemented in C. A Go version named GoFE of the library also exists.
To quickly get familiar with FE, read a short and very high-level introduction on our Introductory Wiki page.
The documentation for CiFEr is available on GitHub Pages.
CiFEr is distributed under the Apache 2 license. It uses GMP, which is distributed under the dual licenses, GNU LGPL v3 and GNU GPL v2.
Please note that the library is a work in progress and has not yet reached a stable release. Code organization and APIs are not stable. You can expect them to change at any point.
The purpose of CiFEr is to support research and proof-of-concept implementations. It should not be used in production.
The requirements have to be installed manually (via package manager or building the source code).
CiFEr relies on GMP for all big integer arithmetic. We recommend familiarizing yourself with it before using CiFEr.
To be able to build CiFEr as described below, AMCL must be compiled with BN254 curve. This can be done manually, but for convenience, we provide a Bash script that runs a modified AMCL setup (a Python script) and installs a minimal version of AMCL in the standard directory /usr/local/lib
and header files in /usr/local/include
. These default values can be changed in external/amcl/setup_amcl.sh
. To use the script, run:
Alternatively, if you do not like to pollute /usr/local/
with unmanaged files, you can use a locally compiled AMCL through a submodule:
The above script takes care of compiling AMCL, and places it where cmake
can find it later.
To build and install, first download it, then run the following commands in the source code directory:
This builds the shared library (libcifer.so
) and installs it. By default, it places the shared library in /usr/local/lib
and the header files in /usr/local/include
(For this, you will need to run the command as superuser). To set a custom install directory (e.g. an install
directory in the root of the repo) instead of /usr/local
, pass it to the cmake command, e.g.:
The build commands also create an executable which runs all unit tests. To make sure the library works as expected, run
Note that this command also builds the library and test executable if they have not been built yet.
We provide a simple Docker build for trying out the library without worrying about the installation and the dependencies. You can build a Docker image yourself by running (possibly with sudo)
or downloading it from Docker Hub
In the file example/example.c
you will find a dummy code using CiFEr library. Modify it as you wish and then run
where $PATHTOCIFER
is your absolute path to CiFEr library (something like /home/username/CiFEr
). This will link the example
folder in your repository with the one in the Docker image. Then it will compile example.c
code and execute it. See the instructions bellow on how to use schemes implemented in CiFEr or check out any of the tests implemented in test
folder.
After you have successfuly built and installed the library, you can use it in your project. Instructions below provide a brief introduction to the most important parts of the library, and guide you through a sequence of steps that will quickly get your FE example up and running.
To use the library, you must #include
its headers in your source code and link it with the -lcifer
flag when building your code.
You can choose from the following set of schemes:
You will need to include headers from innerprod
directory.
We organized implementations in two categories based on their security assumptions:
cfe_ddh
) and LWE (cfe_lwe
).cfe_ring_LWE
). cfe_ddh_multi
).cfe_damgard
- similar to cfe_ddh
, but uses one more group element to achieve full security, similar to how Damgård's encryption scheme is obtained from ElGamal scheme (paper)), LWE (cfe_lwe_fs
) and Paillier (cfe_paillier
) primitives.cfe_damgard_multi
).cfe_dmcfe
).cfe_damgard_dec_multi
).cfe_fhipe
).cfe_fh_multi_ipe
).You will need to include headers from quadratic
directory.
It contains an implementation of an efficient FE scheme for quadratic multi-variate polynomials by Sans, Gay and Pointcheval (paper) which is based on bilinear pairings, and offers adaptive security under chosen-plaintext attacks (IND-CPA security).
You will need to include headers from abe
directory. There are three implemented schemes:
cfe_fame
.cfe_gpsw
.cfe_dippe
.These schemes allow to specify a decryption policy defining which attributes are needed to be able to decrypt. For the latter we implemented a policy converter which accepts a boolean expression defining the policy and outputs a monotone span program (MSP) which can be used as an input for the ABE schemes.
All CiFEr schemes are implemented as C structs + functions which operate on them with (at least logically) similar APIs. So the first thing we need to do is to create a scheme instance by initializing the appropriate struct. For this step, we need to pass in some configuration, e.g. values of parameters for the selected scheme.
Let's say we selected a cfe_ddh
scheme. We create a new scheme instance with:
In the last line above, the first argument is length of input vectors x and y, the second argument is bit length of prime modulus p (because this particular scheme operates in the ℤp group), and the last argument represents the upper bound for elements of input vectors.
However, configuration parameters for different FE schemes vary quite a bit. Please refer to library documentation regarding the meaning of parameters for specific schemes. For now, examples and reasonable defaults can be found in the test code.
After you successfully created a FE scheme instance, you can call the relevant functions for:
All CiFEr chemes rely on vectors (or matrices) of big integer (mpz_t
) components.
CiFEr schemes use the library's own vector (cfe_vec
) and matrix (cfe_mat
) types. They are available in the data
directory. A cfe_vec
is basically a wrapper around an array of mpz_t
integers, while a cfe_mat
is a wrapper around an array of cfe_vec
vectors.
In general, you only have to worry about providing input data (usually vectors x and y). Each element in a vector or matrix can be set by calling their respective _set
function, for example:
For matrices, you can set whole rows to contain the same values as a vector.
To generate random mpz_t
values from different probability distributions, you can use one of our several implementations of random samplers. The samplers are provided in the sample
directory. Note that the uniform sampler does not require special initialization while other samplers do. Before performing any random sampling, the function cfe_init
needs to be called to ensure that the system's random number generator has been properly seeded.
You can quickly construct random vectors and matrices by:
Providing the data structure and sampler as an argument to the relevant _sample_vec
or _sample_mat
functions. ```c cfe_vec v; cfe_mat m; cfe_vec_init(&v, 5); cfe_mat_init(&m, 2, 3); cfe_normal_cumulative_sample_vec(&v, &s); // sets all elements of the vector to random elements cfe_normal_cumulative_sample_mat(&m, &s); // sets all elements of the matrix to random elements
// Uniform sampler (does not need to be initialized) mpz_t max; mpz_init_set_ui(max, 10); cfe_uniform_sample_vec(&v, max); ```
In the following we give some examples how to use the schemes. Note that every scheme has an implemented test in the folder test
, in which you can see how to use the scheme and modify it to your needs.
Please remember that all the examples below omit error handling. All functions which can fail return a cfe_error
(its definition is in errors.h
header, located in the internal
directory) which is non-zero if the function encountered an error.
Additionally, all examples also omit memory freeing. In CiFEr, all functions which allocate memory for their results (passed as input parameters) have the suffix _init
and have a corresponding function with the suffix _free
. All other functions expect their inputs to be already initialized and do not allocate any memory the user would need to free manually.
The example below demonstrates how to use single input scheme instances. Although the example shows how to use the cfe_ddh
scheme from directory simple
, the usage is similar for all single input schemes, regardless of their security properties (s-IND-CPA or IND-CPA) and instantiation (DDH or LWE).
You will see that three cfe_ddh
structs are instantiated to simulate the real-world scenarios where each of the three entities involved in FE are on separate machines.
This example demonstrates how multi input FE schemes can be used.
Here we assume that there are numClients
encryptors (ei), each with their corresponding input vector xi. A trusted entity generates all the master keys needed for encryption and distributes appropriate keys to appropriate encryptor. Then, encryptor ei uses their keys to encrypt their data xi. The decryptor collects ciphers from all the encryptors. It then relies on the trusted entity to derive a decryption key based on its own set of vectors yi. With the derived key, the decryptor is able to compute the result - inner product over all vectors, as Σ <xi,yi>.
Note that above we instantiate multiple encryptors - in reality, different encryptors will be instantiated on different machines.
In the example below, we omit instantiation of different entities (encryptor and decryptor).
In the example below we demonstrate a usage of ABE scheme FAME. We omit instantiation of different entities (encryptor and decryptor). We want to encrypt the following message msg so that only those who own the attributes satisfying a boolean expression 'policy' can decrypt.
In the example below we demonstrate how to serialize public key for ABE scheme GPSW. The serialization is done by using Protobuf library, converting CiFEr structures to a single array of bytes. The serialization is currently available for ABE schemes FAME and GPSW.