Levelled Fully Homomorphic Encryption from Learning with Errors

This is part of a series of posts where we explain how to do fully homomorphic encryption (FHE). FHE lets you encrypt a message, and then other people can perform arbitrary operations on the encrypted message without being able to read the message.

Levelled FHE is a sort of weaker version of FHE. Like FHE, levelled FHE lets you perform operations on encrypted data. But unlike FHE, there will be a limit on the number of operations you can perform before the data must be decrypted.

Loosely speaking, the encryption procedure will involve some sort of “noise” or “error.” As long as the error is not too big, the message can be decoded without trouble. But each operation on the encrypted data will cause the error to grow – and if it grows beyond some maximum error tolerance, the message will be lost. So there is a limit on how many operations you can do before the error gets too big.

As a sort of silly example, imagine your message is a whole number between 0 and 10 (so it’s one of 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10), and your “encryption” scheme encodes the message as a real number that is very close to the message. So if the ciphertext is 1.999832, well then that means the original message was 2. The decryption procedure is “round to the nearest integer.”

(You might be thinking: This is some pretty terrible cryptography, because the message isn’t secure. Anyone can figure out how to round a number, no secret key required. Yep, you’re right. The actual encryption scheme is more complicated. But it still has this “rounding-off-errors” feature, and that’s what I want to focus on right now.)

Now imagine that the “operations” you want to perform are addition. (If you like, imagine doing the addition modulo 11, so if a number gets too big, it “wraps around.”) Well, every time you add two encrypted numbers (), the errors add as well. After too many operations, the error will exceed , and the rounding procedure won’t give the right answer anymore.

But as long as you’re careful not to go over the error limit, you can add ciphertexts with confidence.

The main idea: Approximate eigenvalues

If you haven’t already, this might be a good time to go back and read about the learning with errors problem and how you can use it to do public-key cryptography.

You should at least understand the vague idea: We’re going pick some large integer (in practice could be anywhere from a few thousand to ), and do “approximate linear algebra” modulo . In other words, we’ll do linear algebra, where all our calculations are done modulo – but we’ll also allow the calculations to have a small “error” , which will typically be much, much smaller than .

Here’s the setup. Our secret key will be a vector – a vector of length , where the entries are integers modulo . Suppose we want to encode a message that’s just a single bit, let’s say . Our ciphertext will be a square -by- matrix such that

Now if we assume that has at least one “big” entry (say ), then decryption is easy: Just compute the -th entry of , and determine whether it is closer to or to .

With a bit of effort, it’s possible to make this into a public-key cryptosystem. The main idea is to release a table of vectors such that , and use that as a public key. Given and the public key, you can find a matrix such that – then take , where is the identity matrix. And can be built row-by-row… but we won’t get into the details here.

Indeed homomorphic encryption is already interesting without the public-key feature. If you assume the person encrypting the data knows , it’s easy (linear algebra, again) to find such that .

To make homomorphic encryption work, we need to explain how to operate on . We’ll describe three operations: addition, NOT, and multiplication (aka AND).

Addition is simple: Just add the matrices. If and , then

Of course, addition on bits isn’t a great operation, because if you add , you get , and isn’t a legitimate bit anymore. So we won’t really use this.

Negation of a bit (NOT) is equally simple, though. If is a bit, then its negation is simply . And if is a ciphertext for , then is a ciphertext for , since

Multiplication is also a good operation on bits – it’s just AND. To multiply two bits, you just multiply (matrix multiplication) the ciphertexts:

(At this point you might be concerned about this symbol and what happens to the size of the error. That’s an important issue, and we’ll come back to it.)

Anyway, once you have AND and NOT, you can build arbitrary logic gates – and this is what we mean when we say you can perform arbitrary calculations on your encrypted bits, without ever learning what those bits are. At the end of the calculation, you can send the resulting ciphertexts back to be decrypted.

A constraint on the secret key and the “Flatten” operation

In order to make the error estimates work out, we’re going to need to make it so that all the ciphertext matrices have “small” entries. In fact, we will be able to make it so that all entries of are either or .

To make this work, we will assume our secret key has the special form

where .

To see how this helps us, try the following puzzle. Assume (so all our vectors have entries modulo 11), and , so our secret key has the form

You know has this form, but you don’t know the specific value of .

Now suppose I give you the vector I ask you for another vector where has to have the following two properties:

And you have to find this vector without knowing .

The solution is to use binary expansion: take . You should check for yourself to see why this works – it boils down to the fact that is the binary expansion of .

How would you flatten a different vector, like

I’ll leave this as an exercise to you! As a hint, remember we’re working with numbers modulo 11 – so if you come across a number that’s bigger than 11 in your calculation, it’s safe to reduce it mod 11.

Similarly, if you know has the form

and you are given some matrix with coefficients in , then you can compute another matrix such that:

The process is essentially the same binary-expansion process we used above to turn into , applied to each entries of each row of the matrix .

So now, using this operation, we can insist that all of our ciphertexts are matrices with coefficients in . For example, to multiply two messages and , we first multiply the corresponding ciphertexts, then flatten the resulting product:

Of course, revealing that the secret key has this special form will degrade security. This cryptosystem is as secure as an LWE problem on vectors of length , not . So we need to make bigger, say , to get the same level of security.

Error analysis

Now let’s compute more carefully what happens to the error when we add, negate, and multiply bits. Suppose where is some vector with all its entries bounded by a bound . (And similarly for and .)

When we add two ciphertexts, the errors add:

So the error on the sum will be bounded by .

Negation is similar to addition – in fact, the error won’t change at all.

Multiplication is more complicated, and this is why we insisted that all ciphertexts have entries in . We compute

Now since is either or , we know that is a vector with all entries bounded by . What about ? Here you have to think for a second about matrix multiplication: when you multiply an -by- matrix by a vector, each entry of the product comes as a sum of different products. Now we’re assuming that is a matrix, and all entries of are bounded by … so the product has all entries bounded by . Adding this to the error for , we get that the total error in the product is bounded by .

In summary: We can start with ciphertexts having a very small error (if you think carefully about this protocol, you will see that the error is bounded by approximately ). Every addition operation will double the error bound; every multiplication (“and” gate) will multiply it by . And you can’t allow the error to exceed – otherwise the message cannot be decrypted. So you can perform calculations of up to approximately steps. (In fact, it’s a question of circuit depth: you can start with many more than input bits, but no bit can follow a path of length greater than AND gates.)

This gives us a levelled fully homomorphic encryption protocol. Next we’ll see a trick called “bootstrapping,” which lets us turn this into FHE.