Installation/compilation of HElib is quite easy if you get things right. First of all you will need NTL (number theory library). I tried using the default version of NTL for my version of Ubuntu, but HElib wouldn’t compile. So, get the newest version 6.0.0. Compile and install (./configure; make; make check; make install).
Then head over to github and clone the git repository (git clone https://github.com/shaih/HElib.git). Compilation of HElib is pretty easy (cd HElib/src; make). This will build a library against which you can statically compile your own code which can use HElib. There are, however, also some test programs you might be interested in (named Test_*). To compile these do (make test).
Within Test_General.cpp we can learn a lot about HElib and how to use it in code. First off is the main function. There are a lot of parameters. Running (./Test_General_x –help) explains what these parameters are (to some degree). I’ll go in more detail on some of them here. The parameters are:
R is the number of rounds
p is the plaintext base [default=2]
r is the lifting [default=1]
d is the degree of the field extension [default==1]
(d == 0 => factors[0] defined the extension)
c is number of columns in the key-switching matrices [default=2]
k is the security parameter [default=80]
L is the # of primes in the modulus chain [default=4*R]
s is the minimum number of slots [default=4]
m is a specific modulus
R is the number of rounds of testing (basically a parameter to a for loop around the testing). p is the plaintext base. Basically this is the plaintext space. Setting p=2 basically says that plaintexts are in {0,1}. p should be a prime number. d as it says is the degree of the field extension.GHS12 explains (section 2.3) what this is:
We note that the values in the plaintext slots are not just bits, rather they are polynomials modulo the irreducible F_j’s, so they can be used to represents elements in extension fields GF(2^d).
k as said, is the security parameter. A default of 80 is 80 bits of security. The length of the modulus chain, L, allowes for “leveled” FHE. L reduces or eliminates the amount of bootstrapping (see BGV11). Finally, s tells how many (at a minimum) number of plaintext slots you’d like. Set this too high and it will complain.
The basic parameters given in the file are pretty good for testing. Take a look at the GHS12 paper for more on parameter tuning. In that paper they look specifically at tuning BGV for AES.
HElib requires that you setup a context using the FHEcontext class to get things going. This stores all the parameters, etc needed for other tasks you will want to perform (key generation, encryption, decryption, homomorphic operations). I’ve been doing this in my code as follows:
#include long p=101; long r=1; long L=4; long c=2; long k=80; long s=0; long d=0; long w=64; long m = FindM(k, L, c, p, d, s, 0); FHEcontext context(m,p,r);
I explained the parameters in my last post so I won’t go into much detail. The FindM function (FHEContext.h) is something new. I’m not going to go into the details of it (as I’m still trying to understand it all), but basically it will find a valid value for m given the specified values. In addition to setting up the context, we also need to build the modulus chain (this is what recent versions of BGV use to avoid bootstrapping). We also need a value G which is the monic polynomial used in constructing the ring BGV works in:
buildModChain(context, L, c);
G = context.alMod.getFactorsOverZZ()[0];
Once you have all this setup, generating public and private keys in HElib is pretty easy:
FHESecKey secretKey(context); const FHEPubKey& publicKey = secretKey; secretKey.GenSecKey(w);
The value w is the hamming weight to use in key generation (see appendix B of GHS12 for an explanation).
Now that we have the key pair established, we can encrypt and decrypt. HElib uses theEncryptedArray class for data movement, so make sure you #include
EncryptedArray ea(context, G);
We then use a PlaintextArray to represent a plaintext. The documentation shows all the various ways this can be done. I’m going to show a simple (yet inefficient) way to encrypt a single value:
Ctxt c(publicKey); PlaintextArray p(ea); p.encode(5); ea.encrypt(c, publicKey, p);
This will encrypt the number 5 and store it in c. Decryption is very simple too:
PlaintextArray p_decrypted(ea); ea.decrypt(c, secretKey, p_decrypted);
The reason for using a PlaintextArray is that BGV supports what is being called SIMD (single-instruction, multiple-data) operations. To understand this, consider a simple example. Say we have 20 pairs of numbers (a1,b1) to (a20, b20) and we want to multiply each pair together to get c1…c20. Clearly this can be done by computing (a1*b1), (a2*b2), and so on. BGV, however, will allow us to store all a1…a20 in a single ciphertext (say a) and all b1…b20 in a single ciphertext (say b). Then we only need to compute c=a*b. When we decrypt c we can access c1…c20. Thus a single multiplication can actually multiply many values at once.
The ciphertext values are stored in instances of Ctxt. Take a look at that class’s documentation to see the supported operations. The most basic are overloads of operator+=, operator-=, and operator*=. Using those you can operate on the ciphertexts to compute functions. You should be able to do as many additions and subtractions as you want. If, however, you do too many multiplications on the same ciphertext value, the ciphertext becomes too noisy to decrypt. More on that in the next post.
To demonstrate HElib and to be able to better play with some of the settings, I’ve written a simple application which implements a RPN calculator (Reverse Polish notation (RPN) is a mathematical notation in which every operator follows all of its operands, eg 3 4 +). You can grab a copy of it on the link https://code.google.com/archive/p/thep/downloads. You’ll have to modify Makefile to point the HElib variable to the proper directory. Compile by typing make and run it by typing ./HEcalc. You’ll then be able to do things like:
3
4
*
q
at which point it should return 12.
This is a very preliminary version of HEcalc. I haven’t done anything with ciphertext packing, etc. The purpose of this code is to demonstrate how to add, subtract, and multiply encrypted numbers (along with how to encrypt, decrypt, etc). Play with it some. You’ll notice that by changing the value of L you’ll be able to do more multiplications without the error becoming too great to properly decrypt. You can do as many additions as you want (basically) as the error grows very slowly with additions. Another thing you’ll see when playing with this is that when you decrypt a value, you actually get a number of copies of that value. This is due to ciphertext packing (i.e., storing many values in the same ciphertext). This allows you to do SIMD operations. I hope to detail ciphertext packing and SIMD in the next post. Until then, feel free to use the comments to ask questions about HEcalc and I’ll answer as best as I can.