Secure Aggregation for Federated Learning with Homomorphic Encryption

Federated learning makes a beautiful promise: the data never leaves home. Hospitals, phones or banks train a shared model on their own private data, and send only their model updates to a central server, which averages them into a better global model. No patient records, no keystrokes, no transactions are ever uploaded. The catch is that model updates are not as innocent as they look. They are computed from the data, and researchers have shown in Deep Leakage from Gradients that the training data itself can be reconstructed from shared gradients, sometimes pixel by pixel.

Granite gravel stones viewed from above, showcasing a detailed natural texture by pexels

So the server shouldn’t see individual updates either. Here is the good news: it never needed them in the first place, only their average. Secure aggregation gives the server exactly that and nothing more: clients encrypt their updates, the server adds them up without decrypting them, and only the sum is ever decrypted. In this post, we’ll build it with homomorphic encryption using LightPHE in Python, Go, Java and TypeScript, and finish with a model that learns from four clients without anyone seeing their data or their updates.


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

Cryptography Basiscs From Scratch In Python

Federated Learning in 30 Seconds

The most common recipe is federated averaging, from Communication-Efficient Learning of Deep Networks from Decentralized Data, the paper that coined the term federated learning. Each round looks like this:

1. server sends the current global model to the clients
2. each client trains it on its own data for a while, and gets a local model
3. each client sends its local model, or the difference to the global model, to the server
4. server averages them:  global model = (local model_1 + ... + local model_k) / k
5. repeat

Step 4 is the only place the server touches the updates, and it is a plain sum divided by a public count. A sum is exactly what additively homomorphic encryption is good at.

Required Homomorphic Features

1. Additive homomorphism. Multiplying ciphertexts adds the plaintexts, so the server turns E(u_1), E(u_2), ..., E(u_k) into E(u_1 + u_2 + ... + u_k) with the public key alone. The number of clients is public, so dividing the decrypted sum by it gives the average.

2. Quantization. Model weights are floats, while homomorphic encryption works on integers. Each value is scaled by 10^5 and rounded, keeping 5 decimal digits, and every client declares a max_value, so all quantized values fall into a known range [-B, B]. They are then shifted by B to be non-negative.

3. Packing, the feature that makes it practical. A real model has thousands or millions of weights. Encrypting each one separately would be painfully slow, and a waste: a 1024-bit Paillier plaintext can hold a number with over 300 decimal digits, while a quantized weight needs about 6. So LightPHE packs many values into one plaintext, each in its own fixed-width slot, the way BatchCrypt does for cross-silo federated learning:

plaintext = (x_1 + B) + (x_2 + B) * 2^s + (x_3 + B) * 2^(2s) + ... + (x_40 + B) * 2^(39s)

each slot is s bits wide, with room for the sum of max_clients values:  s = bits(max_clients * 2B)

Adding two packed ciphertexts adds every slot at once, like 40 additions for the price of one. The slots are sized with headroom for max_clients updates, so a sum never overflows from one slot into the next, and the server refuses to sum more updates than that. With 1024-bit keys, 40 values fit into a ciphertext. On my laptop, encrypting an update of 1000 values took 0.49 seconds as 25 packed ciphertexts, against 19.85 seconds as 1000 separate ones—about 40 times faster, and 40 times less data to send.

4. Threshold decryption with proofs. Somebody has to decrypt the sum, and it must not be someone who could also decrypt individual updates. So the private key is split among a committee, e.g. 5 independent organizations, any 3 of whom decrypt the sum together, as we covered in our previous post on splitting a private key into pieces. Each member proves its partial decryption, so a dishonest member can’t slip a manipulated average into the global model. And the members only ever decrypt aggregates.

5. Tolerance to dropouts. Phones go offline, hospitals have outages. A client that drops out is simply not summed, as the sum keeps its own count, and the committee still decrypts the sum as long as 3 of its 5 members show up.

This isn’t the only way to do secure aggregation. Google’s protocol by Bonawitz et al., Practical Secure Aggregation for Privacy-Preserving Machine Learning, takes a different route, with pairwise random masks between clients that cancel out in the sum instead of encryption. The homomorphic route is simpler to reason about and suits a modest number of clients, such as hospitals or banks, very well.

Which Algorithms Can Be Used, and Why

Secure aggregation needs a large plaintext space that decrypts directly, for packing, and threshold decryption, for the committee. Only two cryptosystems in LightPHE have both. I tried the others, and LightPHE refused them with does not support secure aggregation. Use Paillier or Damgard-Jurik instead:

AlgorithmsSecure aggregationWhy
Paillier✅Large plaintext space with direct decryption, so 40 values fit into a 1024-bit ciphertext, and threshold decryption with proofs when keys are generated with safe_primes = True
Damgard-Jurik✅Same as Paillier, with an even larger plaintext space: 53 values per ciphertext with default keys in my run
Exponential ElGamal, Elliptic Curve ElGamal❌They support threshold decryption, but decrypting means solving a discrete logarithm, which is only feasible for small numbers, never for a packed plaintext of hundreds of digits
Okamoto-Uchiyama, Benaloh, Joye-Libert, Bresson-Catalano-Pointcheval❌Additively homomorphic, but LightPHE has no threshold decryption for them, so there could be no committee
Damgard-Geisler-Kroigaard❌A 32-bit plaintext space is far too small for packing, and no threshold decryption either
RSA, ElGamal, Goldwasser-Micali and others❌Not additively homomorphic, so they cannot sum updates at all

Example 1: One Round Among Hospitals

Three hospitals train a tumor detection model on their own scans. A committee of 5 independent organizations holds the shares of the private key, any 3 of which can decrypt. The hospitals encrypt their model updates, the server adds them up, and the committee decrypts only the sum. We keep the model to 3 weights to make the numbers readable, and then encrypt a realistic update of 1000 weights to see packing at work.

The committee gets the average update [0.07, -0.08, 0.37] and the count of 3, and nothing else: not which hospital contributed what, not even whether one of them sent an outlier. The 1000-weight update fits into 25 ciphertexts, which is what each hospital uploads instead of 1000. Java snippets use assert for brevity, so run them with -ea to enable assertions.

Example 2: Training a Model Without Seeing Anyone’s Data

Now let’s run real federated learning, round after round. Four clients hold different slices of private data, all following the hidden relationship y = 2x - 1, and want to learn a linear model y = w x + b together. In each round, every client takes a gradient descent step on its own data, encrypts its local model, and the committee decrypts only the average, which becomes the next global model.





After 30 rounds, the global model is w = 2.00 and b = -1.00, the hidden relationship, learned in about 5 seconds in Python. Quantization keeps 5 decimal digits, which is plenty for training, and the result matches federated averaging without encryption. Along the way, no client saw another client’s data, and nobody saw a single client’s local model—not the server, and not the committee.

More Scenarios

  • Medical imaging: Hospitals train a tumor detection model on their own scans, and a central server averages their encrypted updates, so neither the server nor the other hospitals see any hospital’s update, which could leak information about its patients.
  • Keyboard prediction: A keyboard app improves next-word prediction from many phones, and a committee of independent organizations decrypts only the average update of each round, so no single party, including the app maker, can inspect the update of a particular phone.
  • Fraud detection: Banks build a shared fraud detection model without pooling their customers’ transactions, and verified partial decryptions stop a dishonest committee member from slipping a manipulated average into the shared model.
  • Energy forecasting: Smart meters train a consumption forecasting model at home, and the utility learns only the averaged model, not the daily routine that a household’s own update would reveal.
  • Beyond machine learning: The same pipeline sums any vectors privately, e.g. companies computing industry-wide salary averages per role, or retailers computing total sales per product category, where each participant’s numbers stay encrypted.

Caveats

  • The average is revealed by design: Secure aggregation hides individual updates, not the aggregate. With only 2 clients, each one can subtract its own update from the average and learn the other’s. Require a minimum number of clients per round, and consider adding differential privacy noise when the aggregate itself is sensitive.
  • Encryption hides poisoned updates, too: The server can no longer inspect updates, so it can’t spot a malicious client’s poisoned one either. max_value is checked when an honest client encrypts its update, but a malicious client running modified code could still encrypt a crafted value. Zero-knowledge range proofs can close that gap.
  • The usual threshold caveats: The dealer knows the private key while splitting it, and 3 colluding committee members could decrypt an individual update. Choose members with competing interests, and have them refuse to decrypt anything but aggregates.
  • Everyone must agree on the layout: Clients must use the same key, update length, max_value and max_clients, otherwise the server refuses to sum their updates.

Conclusion

Federated learning keeps the data at home, but the updates still leave, and they can leak the data. Secure aggregation closes that gap by letting the server compute the only thing it needs, the average, without seeing a single update. It takes additive homomorphism for the sum, quantization for floats, packing to turn a thousand encryptions into 25, and threshold decryption with proofs so that only a committee, and only the sum, is ever decrypted. Paillier and Damgard-Jurik are the two cryptosystems in LightPHE that offer all of this.

Our four clients learned y = 2x - 1 together without showing anyone their data or their updates. Swap the linear model for a neural network, and the pipeline stays the same—in Python, Go, Java or TypeScript.


Support this blog financially if you do like!

Buy me a coffee      Buy me a coffee


Leave a Reply