← Demos Index
FHE in Action · Encrypted Query
Demo 07 · RWE Pull
Factual Demonstration

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.

Step 01 · Setup

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

params := bgv.NewParametersFromLiteral({
  LogN: 13,  // N = 8192
  LogQ: [55,45,45],
  PlaintextModulus: 65537,
})
8 192Slots/ciphertext
EXACTScheme type

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).

Step 02 · Keys

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).

~10 msTotal generation

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.

Step 03 · Database

Encrypt the database.

The pharmacy encrypts the 3 characteristic vectors ONCE (diabetic, started metformin, dropped out). Any future query reuses those ciphertexts.

Patient Database

1 000Patients
3Flags per patient

Realistic distribution: ~30% diabetic, ~12% started metformin, ~3% dropped out within 30d.

Encryption

9 msTotal encryption (3 vectors)
384 KBPer encrypted vector
Step 04 · Query

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

SELECT count(*)
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

// triple AND = multiplication
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.

Step 05 · Computation

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

2Encrypted multiplications
~10Galois rotations
47 msTotal time
0Bytes decrypted

A single ciphertext: ct_response. It encodes the count. The pharmacy also cannot decrypt it — only pharma holds the secret key.

Step 06 · Response

Pharma decrypts.

Pharma receives ct_response. Decrypts it with its secret key. Learns only the count.

Query response
33
diabetic patients who started metformin and dropped out within 30 days
Step 07 · Proof

Mathematical validation.

BGV is an exact scheme — no approximation noise like CKKS.

FHE vs Plaintext

MetricFHEPlaintext
Count3333
DifferenceEXACT — 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.

Step 08 · Adversarial

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.

Step 09 · Summary

What happened.

In under 100 ms, pharma obtained a regulatory-grade insight without ever seeing individual patients.

The Flow

  1. Pharmacy encrypted its 1 000-patient database once
  2. Pharma defined a SQL-like query over 3 flags
  3. Pharmacy ran the entire query under encryption
  4. Pharma decrypted only the aggregated count

Real Numbers

9 msDatabase encryption
47 msQuery
384 KBPer ciphertext
EXACTPrecision
4 eBooks use this primitivePharmacies, Labs, Pharma and Banks (M&A due diligence).
Didactic simplification · temporal windowsThis demo uses simple binary flags. Real RWE queries include temporal windows: "in the last 90 days", "in patients ≥ 65 years", "after event X". Temporal windows require extra encoding: each date becomes a binary "month/year" vector and the query uses AND/OR between vectors. The FHE primitive stays identical; only the input representation becomes richer. For exact counts BGV is the right scheme; for statistical aggregations with windows CKKS is more efficient.
PT EN