The jeweller who works in gloves.
Picture a locked glass box with built-in gloves. You put the jewels inside, lock it and hand it to the jeweller. He can polish, cut and set them through the gloves, but he never holds the jewels, because he has no key. When he is done he hands the box back, and only you open it.
That is what homomorphic encryption (FHE) does with numbers: the server adds and multiplies the locked data and returns a locked result. Only the key holder can read it. The maths below explains how the "gloves" work and why they have a limit.
Clock arithmetic.
On a clock, 11 o’clock plus 2 hours is 1 o’clock, not 13: past 12, it starts over. Mathematicians call this arithmetic "modulo 12". Every FHE operation works like this, on a huge clock, and that is what keeps locked numbers the same size no matter how many operations you do.
To store a message, the clock is split into slices, one per possible value. In the specimen above there are 64 positions and 4 slices of 16: storing the number 2 means pointing at the middle of the third slice. Reading means seeing which slice the hand landed in. As long as the hand stays inside its slice, the reading is exact.
A grid that is easy to hide in, hard to search.
The lock rests on a geometry problem. Think of a slanted grid of points, like the stakes of a crookedly planted orchard. Someone secretly picks a grid point and publishes a point slightly shifted from it. The question: which grid point was it?
In two dimensions anyone finds it by eye. With hundreds of dimensions, nobody knows a fast way, not even with a quantum computer. The technical name is "lattice" (and the problem, "learning with errors", LWE). NIST’s new post-quantum locks rest on the same kind of problem.
b = a·s + e + Δ·m
Read aloud: "the published number b is a random number a times the secret s, plus a little noise e, plus the message m stretched to the middle of its slice (Δ)." Whoever has s subtracts a·s and is left with just the message plus a little noise. Whoever does not sees a number that looks random.
Noise protects, and grows with every operation.
The noise e is what makes the lock safe, but it does not go away: it travels with the operations. Adding two locked numbers adds their noise, and the hand drifts a little off centre. Multiplying multiplies the noise, and the hand jumps.
That is why, in practice, what limits an encrypted computation is not the number of additions, it is how many multiplications in a row fit before the hand leaves its slice. When it does, the key reads the neighbouring slice’s number. Try it in the specimen: encrypt 1 and multiply by 3 twice.
On our bench, with 128-bit parameters, the integer dialect (BFV) gets the 7th multiplication in a row right and gets the 8th wrong without warning: it returns 28323 instead of 282. No error, no exception, just a plausible wrong number.
Exact, decimal or bit by bit.
The same idea turns into three "dialects", each good at one thing:
Exact integers (BGV/BFV): whole-number arithmetic on a clock, with no rounding. This is what counts votes.
Decimals (CKKS): here noise is treated as the last digits of a rounding, like on a calculator. It suits averages and AI models. On our bench it loses about 0.58 bits of precision per multiplication level.
Bit by bit (TFHE): locks each bit and builds logic gates (AND, OR, NOT). It is the one that can compare and decide. It is the opposite of CKKS: it compares 8 bits in ~2 s (CKKS takes ~5 min), but a 2,048-number dot product would take ~190 h (CKKS does it in 115 ms).
One box, 16,384 cells.
A single lock holds not one number but a whole row: in our voting demo, 16,384 cells in the same box. Adding boxes adds every cell at once, like a spreadsheet where one formula fills the whole column.
A vote becomes a row with a 1 in the chosen candidate’s cell and 0 elsewhere. Adding a thousand locked rows gives each candidate’s total in their cell, without opening any vote.
The expensive part is not adding or multiplying: it is moving data between cells, for instance to add the whole row into one number. On our bench a multiplication took 2.7 ms and folding the row (12 rotations) took 112.8 ms, about 40 times more.
Cleaning the noise is expensive.
When the noise nears the limit you can run a "clean-up" (bootstrap): the server runs, still locked, the very procedure for opening the lock, using a locked copy of the key. Out comes the same number with small noise again. In principle this allows computations of any length.
The price, measured on our bench at real 128-bit security: 1 min 18 s ± 8 s per clean-up and 10.26 GB of keys held on the server. With toy parameters it is 1.92 s and 840 MB, about 40 times less. Many published benchmarks do not say which of the two they measure.
A shortcut we measured: instead of the clean-up, the server sends the tired number back to the owner, who opens it, locks it again and sends it back. It takes 1.04 s at 100 Mbps, with no clean-up key at all (75 times cheaper), but it needs the owner online.
And a warning: with honest 128-bit parameters at the smaller size (logN=13), 2 multiplications in a row fit, not 8, as weaker settings suggested.
Locking is not enough: you must prove.
An encrypted election needs four more ideas, all with the same "prove without showing" flavour:
Zero-knowledge proof. The voter proves their row holds exactly one 1 and zeros elsewhere, without showing where the 1 is. It is like proving you know the safe’s combination by opening and closing it behind a curtain.
Blind signature. The registry stamps the right to vote inside a carbon-paper envelope: the stamp goes through, but the registry never sees what it stamped. Later it cannot link the stamp to the voter.
Split key, 3 of 5. The key that opens the result is split among five people; any three together can open it, two learn nothing. That is the curve beside this: three points rebuild the curve and reveal the secret at zero.
The shuffling ballot box and the fingerprint chain. Three stages shuffle and re-lock the votes so nobody can link a vote to its arrival order (one honest stage is enough). And every vote joins a chain where each link carries the previous one’s fingerprint: changing an old vote breaks every later link.
The numbers behind the analogies.
| Measure | Value | In practice |
|---|---|---|
| Multiply (locked × plain, with rescale) | 2,7 ms ± 0,2 | multiplying is cheap |
| Fold the row (12 rotations) | 112,8 ms ± 0,8 | moving data costs ~40× a multiply |
| Integers (BFV): multiplications in a row | 7 right; the 8th wrong | fails silently: 28323 instead of 282 |
| Integers (BGV), same budget | dies at the 5th | fails loudly: no level left |
| Decimals (CKKS) | 0.58 ± 0.07 bits per level | loses precision gradually |
| Honest budget at 128-bit (logN=13) | 2 levels | not 8, as in weaker settings |
| Clean-up (bootstrap), 128-bit (logN=16) | 1 min 18 s ± 8 s · 10,26 GB | ~40× the toy set (1.92 s · 840 MB) |
| Send back to owner (100 Mbps) | 1,04 s · 0 B of keys | 75× cheaper, needs the owner online |
| Bit by bit (TFHE) × decimals (CKKS) | compare 8 bits: ~2 s × ~5 min | 2,048 dot product: ~190 h × 115 ms |
Own bench (Lattigo v6), same machine, several repetitions; numbers with ± carry the measured spread. Details and all sixteen tests on the FHE frontier; what it means for your case, on maturity.
The deliberate "dirt" that protects the secret. Every operation adds to it; past the limit, the key reads wrong.
Cleaning the noise without opening the lock. Allows long computations, at 1 min 18 s and 10.26 GB at 128-bit.
The grid of points in hundreds of dimensions. Finding the hidden point is the hard problem that holds the lock.
All the section’s words are in the glossary.
Where this explanation simplifies.
The toy is not secure
The specimen uses a 64-position clock and a single dimension. Anyone can open it. It shows the noise, it protects nothing.
Multiplying two locked numbers is subtler
In the toy we multiply by a plain number. Multiplying two locked numbers needs an extra step ("relinearisation") and grows the noise even more.
"Quantum-safe" is a well-founded bet
No fast attack on the grid problem is known, quantum or otherwise. That is what backs the NIST standards, but it is current knowledge, not a mathematical proof.
Numbers from one machine
Timings come from our bench. Another machine or library changes the values; the ratios (40×, 75×) tend to hold.
- Own FHE bench (Lattigo v6): tests T1 (linear algebra), T2 (depth budget), T4 (bootstrap), T8 (TFHE × CKKS), T13 (security ceiling) and T15 (interactive recrypt); every number carries an audited provenance marker.
- Voting demo (FHE + zero-knowledge proof + blind signature + 3-of-5 key + shuffling box): the same as the tutorial at /fhe/en/.
- Regev, "On lattices, learning with errors…" (2005) · Brakerski–Gentry–Vaikuntanathan (2012) · Fan–Vercauteren (2012) · Cheon–Kim–Kim–Song, CKKS (2017) · Chillotti et al., TFHE (2016) · Chaum, blind signatures (1983) · Shamir, "How to share a secret" (1979).