Adding One Multiplication to Additive Homomorphic Algorithms with Catalano-Fiore

Additively homomorphic encryption is a remarkably useful tool with one frustrating limitation. With Paillier, you can add ciphertexts, and multiply a ciphertext with a plain number (scalar multiplication). You can compute sums, averages and weighted scores on data nobody can see. But you can’t multiply two ciphertexts together. That one missing operation rules out a surprising number of everyday computations: the variance of encrypted salaries, the correlation between two encrypted columns, the dot product of two encrypted vectors, or the distance between two encrypted points.

Creative pattern of wooden clothespins on a rustic wooden surface featuring a radial design by pexels

Fully homomorphic encryption solves this, at the cost of heavy machinery. But in Using Linearly-Homomorphic Encryption to Evaluate Degree-2 Functions on Encrypted Data (ACM CCS 2015), Dario Catalano and Dario Fiore showed something delightful: you can bolt exactly one multiplication onto almost any additive scheme, without changing the scheme at all, using nothing more than high school algebra and a one-time pad. In this post, we’ll take that trick apart piece by piece, and then use it with LightPHE in Python, Go, Java and TypeScript.


🙋‍♂️ You may consider to enroll my top-rated cryptography course on Udemy

Cryptography Basiscs From Scratch In Python

Thinking in Degrees

The cleanest way to describe what a homomorphic scheme can compute is the degree of the polynomial it can evaluate on encrypted inputs. Additions and multiplications with plain constants never raise the degree, so an additive scheme evaluates degree-1, or linear, functions. Multiplying two encrypted inputs gives degree 2, and that’s exactly what everyday statistics often need:

Computation on encrypted x, yFormulaDegree
Sum, meanx_1 + x_2 + … + x_n1
Weighted score with plain weightsw_1 x_1 + … + w_n x_n1
Varianceneeds x_1^2 + … + x_n^22
Covariance, correlationneeds x_1 y_1 + … + x_n y_n2
Dot product of two encrypted vectorsx_1 y_1 + … + x_n y_n2
Squared euclidean distance(x_1 – y_1)^2 + … + (x_n – y_n)^22
Product of three encrypted valuesx y z3

Homomorphic schemes form a ladder. Partially homomorphic schemes like Paillier stop at degree 1. Degree-2 schemes, such as Boneh-Goh-Nissim with its bilinear pairings, allow one multiplication. Somewhat homomorphic schemes allow a bounded number of multiplications, and fully homomorphic schemes allow any number, at a steep price. Catalano-Fiore lifts an ordinary additive scheme exactly one rung up, to degree 2.

The Trick, Step by Step

Step 1: Hide the Message Behind a One-Time Pad

Take any additively homomorphic scheme Enc with a public plaintext space of integers modulo M. To encrypt a message m, pick a uniformly random b in that space, and output a pair: the message masked with b in the clear, and an ordinary encryption of b.

pick a random b in [0, M)

ciphertext of m:   ( a, Enc(b) )       where a = m - b mod M

It feels wrong to leave a in the clear, but it reveals nothing: b is uniformly random and secret, so a = m - b is uniformly random too, whatever m is. It’s a one-time pad. The pad itself, b, is protected by the underlying scheme. Anyone with the private key decrypts b and adds it back: m = a + b.

Step 2: Additions Still Work

Adding two such ciphertexts is easy: add the clear parts as numbers, and the encrypted parts homomorphically. Multiplying with a plain constant scales both. Nothing has been gained yet, but nothing has been lost either.

(a1, Enc(b1)) + (a2, Enc(b2))  =  (a1 + a2, Enc(b1) + Enc(b2))       encrypts (a1 + b1) + (a2 + b2) = m1 + m2
k * (a, Enc(b))                =  (k a, k Enc(b))                    encrypts k m

Step 3: Multiply With High School Algebra

Here is where the magic happens. Expand the product of two messages, written as m = a + b:

m1 m2 = (a1 + b1)(a2 + b2) = a1 a2  +  a1 b2  +  a2 b1  +  b1 b2
                             -----     -----     -----     -----
                             plain   plain * enc  plain * enc  enc * enc

Look at the four terms. a1 a2 is a product of two public numbers, so anyone can compute it and encrypt it. a1 b2 and a2 b1 are a public number times an encrypted value, which an additive scheme handles with scalar multiplication. Only the last term, b1 b2, is a product of two encrypted values, which the scheme can’t compute. So the evaluator computes the first three terms under encryption:

alpha = Enc(a1 a2) + a1 * Enc(b2) + a2 * Enc(b1)       encrypts m1 m2 - b1 b2

multiplied ciphertext:   ( alpha, [ (Enc(b1), Enc(b2)) ] )

And the missing b1 b2? The evaluator can’t compute it, but it doesn’t have to. It simply carries the two encrypted pads along. Whoever holds the private key can decrypt them separately and multiply them in the clear.

Step 4: Decrypt

m1 m2 = Dec(alpha) + Dec(Enc(b1)) * Dec(Enc(b2))

That’s the whole construction: the multiplication of two ciphertexts is split into the part an additive scheme can compute, and a leftover that is postponed to decryption time, where it becomes easy.

A Worked Example

Let’s multiply 3 and 4 in a toy plaintext space of integers modulo 100, where the random pads happen to be 17 and 58:

encrypt 3:   b1 = 17,  a1 = 3 - 17 mod 100 = 86       ciphertext (86, Enc(17))
encrypt 4:   b2 = 58,  a2 = 4 - 58 mod 100 = 46       ciphertext (46, Enc(58))

multiply:    a1 a2 + a1 b2 + a2 b1 = 86 * 46 + 86 * 58 + 46 * 17 = 9726 = 26 mod 100
             alpha = Enc(26), and the pair (Enc(17), Enc(58)) travels along

decrypt:     26 + 17 * 58 = 26 + 986 = 1012 = 12 mod 100,  and indeed 3 * 4 = 12

Notice that the evaluator only ever saw 86 and 46, two numbers that look completely random, and never 3, 4 or 12.

Why Only One Multiplication

After a multiplication, the clear part is gone: a multiplied ciphertext is alpha and a list of pairs, all encrypted. The trick depended on having one factor of each term in the clear, so it can’t be repeated. Multiplying two multiplied ciphertexts would need terms like Dec(alpha) * Dec(alpha'), products of encrypted values with no clear factor to lean on. So LightPHE refuses a second multiplication of ciphertexts.

Everything else still works after the multiplication. Two multiplied ciphertexts are added by adding their alpha parts and joining their lists of pairs. A plain constant scales alpha and the first ciphertext of each pair. A non-multiplied ciphertext can be added too, as (a, Enc(b)) is easily rewritten as Enc(a) + Enc(b) with no pairs. So you can compute any sum of products, plus linear terms, plus constants: every degree-2 polynomial.





Why It’s Secure

The transform uses the underlying scheme strictly as a black box: encryption, decryption, addition and scalar multiplication, nothing else. The clear part of a ciphertext is a one-time-padded value, and everything else is an ordinary ciphertext of the underlying scheme. So the transform is exactly as secure as the scheme it wraps. Ciphertexts carry the public key only, so an untrusted cloud can do all the computing.

There is one thing a multiplied ciphertext does reveal: its structure. Each pair shows which two ciphertexts were multiplied together, though not their values. For most outsourced computations, that’s the computation the cloud was asked to do anyway.

The Price: Ciphertexts That Grow

Nothing is free. Every product leaves a pair of ciphertexts behind, and a sum of products keeps all of them. So a dot product of two encrypted vectors of length d carries d pairs, and decrypting it takes 2d + 1 decryptions of the underlying scheme. Here is what that looked like with 1024-bit Paillier keys on my laptop:

CiphertextSerialized size
Plain Paillier ciphertext0.7 KB
Boosted ciphertext, before multiplication1.1 KB
Product of two ciphertexts2.2 KB
Dot product of two vectors of length 100138 KB, computed in 4.2 seconds and decrypted in 2.4 seconds

For statistics over thousands of records, that growth adds up. If you only need a few aggregates, such as a sum and a sum of squares, it’s perfectly fine.

Required Features of the Underlying Scheme

The construction asks surprisingly little of the scheme it wraps, but each requirement rules some schemes out:

1. Additive homomorphism and scalar multiplication, for adding the encrypted pads and multiplying them with the clear parts.

2. A public plaintext modulus, because the encryptor computes m - b mod M itself.

3. Decryption of any value, because the pads are uniformly random over the whole plaintext space, not small numbers.

4. Tolerance for large plain multipliers, because the clear parts that multiply the encrypted pads are uniformly random too.

Which Algorithms Can Be Used, and Why

LightPHE can boost ten of its cryptosystems. For the others, it refuses with a reason, and the reasons map exactly to the requirements above:

AlgorithmsCatalano-FioreWhy
Paillier, Damgard-Jurik, Bresson-Catalano-Pointcheval, Camenisch-Shoup, Elliptic Curve Paillier, Joye-Libert, Castagnos-Laguillaumie, Naccache-Stern, Benaloh, Damgard-Geisler-Kroigaard✅Additive with a public plaintext modulus and direct decryption. Damgard-Geisler-Kroigaard’s plaintext space is only about 32 bits, though, so results wrap around quickly
Exponential ElGamal, Elliptic Curve ElGamal❌Their decryption solves a discrete logarithm, which is infeasible for the uniformly random pads
Okamoto-Uchiyama❌Its plaintext modulus is secret, so the encryptor can’t compute m - b mod M
Regev, Lyubashevsky-Peikert-Regev❌Multiplying with uniformly random constants exceeds their noise budget
Goldwasser-Micali, ML-KEM❌XOR only, without addition or multiplication with a constant
Boneh-Goh-Nissim, Freeman-Boneh-Goh-Nissim, Brakerski-Vaikuntanathan, Gentry-Halevi-Vaikuntanathan, Gentry-Sahai-Waters❌They already support multiplication natively, so there’s nothing to add
RSA, ElGamal❌Not additively homomorphic

Catalano-Fiore vs Boneh-Goh-Nissim

Both give you one multiplication, but in very different ways:

Catalano-FioreBoneh-Goh-Nissim
Where it comes fromA transform on top of an existing additive schemeA dedicated scheme built on bilinear pairings
Plaintext spaceLarge, e.g. 1024 bits with PaillierSmall, as decryption solves a discrete logarithm
Ciphertext size after multiplicationsGrows with every productStays constant
Best forLarge values and a few aggregatesSmall values and many products

Example 1: The Rules of the Game

In LightPHE, Catalano-Fiore is a boost: you build an ordinary cryptosystem with boost = "Catalano-Fiore", and its ciphertexts gain one multiplication. Let’s see the rules in action: one multiplication, then as many additions and multiplications with plain constants as you like, but not a second multiplication.

Java snippets use assert for brevity, so run them with -ea to enable assertions.

Example 2: Standard Deviation of Encrypted Salaries

A company keeps its payroll encrypted in the cloud, and wants the mean and the standard deviation of salaries. The mean needs only a sum, but the variance needs a sum of squares, a degree-2 function: variance = (x_1^2 + ... + x_n^2) / n - mean^2. So the cloud computes two encrypted aggregates, and the company decrypts just those two numbers.

The company learns a mean of 79.67 and a standard deviation of 23.41, in thousands, and the cloud learns nothing. Notice that the cloud doesn’t even need a cryptosystem of its own: Catalano-Fiore ciphertexts carry the public key, so it simply adds and multiplies them.





Example 3: Do Ads Drive Sales? Correlation on Encrypted Data

A retailer keeps its monthly ad spend and sales encrypted in the cloud, and wants to know how strongly they move together. The Pearson correlation needs five sums: of x, of y, of x times y, of x squared and of y squared. Three of them are degree-2. The cloud computes all five under encryption, and the retailer plugs the decrypted sums into the formula.

A correlation of 0.9973 says ad spend and sales move almost in lockstep. As every statistics teacher will remind you, that’s correlation, not causation—but at least the cloud didn’t learn either column to find out.

More Scenarios

  • Encrypted similarity search: A company stores the face embeddings of its employees encrypted in the cloud, and the cloud computes squared distances to an encrypted query, a degree-2 function, as we did in encrypted nearest neighbor search.
  • Recommendations: A user’s encrypted preference vector and an item’s encrypted feature vector are both stored in the cloud, and their dot product scores the match without revealing either.
  • Risk models with interactions: A model uses products of features, such as debt times interest rate, and both features arrive encrypted.
  • Outsourced statistics: Variances, covariance matrices and regression sums over encrypted datasets, computed by a cloud that never sees a record.

Caveats

  • Exactly one multiplication: Degree 3 and above, such as a product of three encrypted values or a cubic polynomial, are out of reach. For those, look at somewhat or fully homomorphic encryption.
  • Ciphertexts grow: Each product adds a pair of ciphertexts. Keep the number of products in a single result moderate, or decrypt partial sums along the way.
  • Structure leaks: Pairs reveal which ciphertexts were multiplied together, though not their values.
  • Integers only: Scale floats to integers first, and remember that a product of two values scaled by 100 is scaled by 10,000. Results wrap around the plaintext modulus, which matters with small plaintext spaces such as Damgard-Geisler-Kroigaard’s.

Conclusion

Catalano-Fiore is one of those ideas that look obvious once you’ve seen them. Hide each message behind a one-time pad and keep the masked value in the clear, expand the product of two messages, compute the three terms an additive scheme can handle, and postpone the fourth to decryption time, where the private key makes it trivial. The result lifts any additive scheme with a public plaintext space and direct decryption to degree 2, with no new assumptions and no changes to the scheme, at the cost of ciphertexts that grow with every product.

That one extra multiplication unlocks variances, correlations, dot products and distances on encrypted data—in a few lines of Python, Go, Java or TypeScript.


Support this blog financially if you do like!

Buy me a coffee      Buy me a coffee


Leave a Reply