The Counter That Does Not Remember.
A global pharma asks. The pharmacy answers. Without anyone seeing individual patients.
Scenario
Pharma wants to know: how many diabetic patients started metformin in the last 90 days and dropped out within 30? The pharmacy has the database; pharma sends an encrypted query.
Problem
The current IQVIA model relies on "anonymization" that is increasingly considered legally fragile. FHE offers a mathematical guarantee.
Guarantee
Pharma only decrypts the aggregated count. Never sees individual patients. The pharmacy never sees the query (the query itself is encrypted). Everything verifiable.
Define the parameters.
Before encrypting the patient database, we choose BGV parameters. Each is a trade-off between security, speed and computational capacity.
Parameters · BGV
What each term means
BGV ("exact" scheme) — Operates over integers. No approximation noise. The result is bit-identical to the plaintext. Ideal for counts, SQL queries and boolean logic — exactly what this RWE demo needs.
8 192 slots — Each ciphertext is a vector of 8 192 packed values. 1 ciphertext holds 8 192 patients simultaneously. A single operation processes all of them. In real production, millions via batching.
Multiplicative depth 2 — How many chained multiplications the ciphertext supports. This demo's query needs 2 (triple AND across 3 binary flags) → depth 2 is enough.
~128 bits of security — Industry standard. Breaking the key would require ~2128 operations.
RLWE base — Ring Learning With Errors. The same mathematical problem that NIST standardized post-quantum cryptography on (ML-KEM, ML-DSA).
Pharma creates, pharmacy receives.
Unlike other cases, here the secret key belongs to PHARMA (the querier). The pharmacy only receives the public key so it can encrypt data under it. An inversion of roles that is common in RWE pull.
Who has what
Pharma — generates the (sk, pk) pair. Keeps sk. Only pharma can decrypt the final response.
Pharmacy — receives pk + rlk + Galois keys. It can encrypt data and process queries, but CANNOT decrypt anything (not even its own database after encryption).
Why this inversion
Pharma defines the question — In RWE pull, pharma is the one who needs the answer. It chooses the query, needs to interpret the result, and decides what to do with it. It makes sense that it holds the key.
Pharmacy loses flexibility — But gains protection: it cannot be compelled to decrypt the database under court order, nor by a rogue employee, nor by an intruder. Technically impossible.
Symmetric vs asymmetric model — For linkage between a health plan and a hospital (symmetric case), we would use threshold with split keys. For a pharma → pharmacy query (asymmetric), the "pharma holds the key" model works better.
Encrypt the database.
The pharmacy encrypts the 3 characteristic vectors ONCE (diabetic, started metformin, dropped out). Any future query reuses those ciphertexts.
Patient Database
Realistic distribution: ~30% diabetic, ~12% started metformin, ~3% dropped out within 30d.
Encryption
The pharma asks.
Pharma defines the RWE question as a SQL-like boolean formula over the flags. Under FHE, the translation is direct: AND becomes multiplication, counting becomes InnerSum.
The query in traditional SQL
FROM patients
WHERE diabetic = 1
AND started_metformin = 1
AND dropped_30d = 1
This is the query pharma wants to run. In traditional SQL, it would require direct access to the pharmacy's cleartext database.
The same query under FHE
ctAnd := evaluator.Mul(
ctDiab, ctMet,
)
ctAnd := evaluator.Mul(
ctAnd, ctDrop,
)
// COUNT = InnerSum over slots
ctCount := evaluator.InnerSum(ctAnd)
Same semantics, different syntax. Multiplying binary ciphertexts is exactly logical AND. Summing the slots is exactly COUNT.
Query runs on the encrypted database.
The pharmacy runs the entire query over the ciphertexts. Every operation is pure math — at no point is the data decrypted. Total time: 47 ms.
What the operation means
2 encrypted multiplications — For a triple AND between 3 binary flags, we need to chain 2 multiplications: ct₁·ct₂, then (ct₁·ct₂)·ct₃. Each multiplication consumes 1 depth level. That is why we configured LogQ with 2 multiplicative levels in Step 01.
log₂(N) Galois rotations — To sum all 1 000 active slots via InnerSum, ~10 rotations are needed in binary-tree style. Each rotation is an encrypted Galois operation using the Galois keys from Step 2.
47 ms total — Real measured time. Compared to a traditional SQL query (~ms), it is 50× slower. Compared to the time for a committee meeting authorizing access to clinical data in the traditional model (weeks), it is 10⁹× faster.
0 bytes decrypted — The pharmacy decrypts nothing. It cannot. It has no key. The result stays as ciphertext until it reaches pharma.
What the pharmacy now holds
A single ciphertext: ct_response. It encodes the count. The pharmacy also cannot decrypt it — only pharma holds the secret key.
Pharma decrypts.
Pharma receives ct_response. Decrypts it with its secret key. Learns only the count.
Mathematical validation.
BGV is an exact scheme — no approximation noise like CKKS.
FHE vs Plaintext
| Metric | FHE | Plaintext |
|---|---|---|
| Count | 33 | 33 |
| Difference | EXACT — 0 error | |
Why it is Exact
BGV operates over integers modulo a prime (65537). For counts within the domain, the FHE result is bit-identical to the plaintext.
Dishonest pharma.
What can pharma try to infer about individuals from the aggregated count?
Attacks
- 1 — Infer individual from countA single count is not injective. To isolate an individual, pharma would need to run targeted queries — blocked by per-query minimum policy (e.g., never report counts < 10).
- 2 — Read encrypted bytesPseudo-random. Nothing recoverable.
- 3 — Brute force2^3000 flag combinations. Infeasible.
Layered defense
Differential privacy can be added to the count (calibrated Gaussian noise), guaranteeing that even with adaptive queries pharma cannot isolate an individual.
What happened.
In under 100 ms, pharma obtained a regulatory-grade insight without ever seeing individual patients.
The Flow
- Pharmacy encrypted its 1 000-patient database once
- Pharma defined a SQL-like query over 3 flags
- Pharmacy ran the entire query under encryption
- Pharma decrypted only the aggregated count