← Demos Index
FHE in Action · Collaborative PSI
Demo 03 · Inter-Bank Fraud Fighting
Factual Demonstration

The Ledger That Stays Closed.

Follow how three Brazilian banks can uncover clients involved in collaborative fraud — without any of the three ever seeing the others' list.

The Scenario

Three banks suspect the same group of taxpayer IDs is moving suspicious amounts across all three simultaneously. Each bank wants to find out WHICH IDs appear in all three lists — without revealing anything else.

The Problem

For legal (LGPD) and competitive reasons, no bank can show its list to the others. PSI under FHE solves this mathematically.

The Guarantee

The neutral server never sees any of the lists. The ONLY information that comes out is the intersection, collectively decrypted. Mathematical guarantee (RLWE), not political.

Everything you see is real Numbers, timings, sizes and detected IDs were captured from a real execution of demo 03/main.go using Lattigo v6 with the BGV scheme. Nothing is simulated.
Step 01 · Setup

Define the parameters.

Before any encryption, we choose the FHE scheme parameters. Each parameter is a trade-off between security, speed and computational capacity.

Chosen Parameters · BGV

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

What each term means

BGV ("exact" scheme) — A homomorphic encryption family that operates over integers. It has no approximation noise. The result is always bit-identical to the plaintext. Ideal for counts, SQL queries and boolean logic — exactly what PSI needs.

8 192 slots — An FHE ciphertext is not a single value: it is a vector of 8 192 packed values. You operate on all 8 192 at once with a single operation. This native "batching" is what makes FHE viable at scale — in production, millions of IDs fit per ciphertext.

Multiplicative depth 2 — How many chained multiplications a ciphertext supports before becoming unstable. Each multiplication consumes 1 level. 3-party PSI needs 2 chained multiplications → depth 2 is enough.

~128 bits of security — Industry standard. Brute-forcing the key would require ~2128 operations — an astronomical number, infeasible even on foreseeable quantum hardware.

RLWE base — Ring Learning With Errors. The mathematical problem underpinning security. It is the same problem over which NIST standardized ML-KEM and ML-DSA, the next generation of post-quantum cryptography.

Step 02 · Keys

Generate the key pair.

In production, this key would be generated via threshold cryptography across the 3 banks + a neutral authority. Here we simplify with a single key for clarity.

Key Flow

Bank A
Threshold
k-of-n
Banks B+C

Quorum required to decrypt the final result. No single party can see anything alone.

What Was Generated

~10 msKey generation
7.0 KBPublic key (~)
44 KBRelinearization (~)
128 bitSecurity
Step 03 · Lists

Each bank generates and encrypts.

Each bank produces a binary characteristic vector (1 = ID in the list) and encrypts locally. ONLY the ciphertext leaves the bank.

Cleartext Lists (didactic reference only)

  • Bank A (BB)007 023 089 142 200
    + 5 own IDs
  • Bank B (Itaú)007 023 089 142 200
    + 5 own IDs
  • Bank C (Bradesco)007 023 089 142 200
    + 5 own IDs
Expected intersection
ID#007 · ID#023 · ID#089 · ID#142 · ID#200

BGV Encryption

3.0 msPer bank
32 BPlaintext (256 bits)
384 KBCiphertext
~12 000×Overhead

The overhead is the price of mathematical privacy. In exchange, the other banks will never see this list.

Step 04 · Transit

What the neutral server receives.

The 3 banks send only their ciphertexts. The neutral server NEVER receives the secret key.

Real Ciphertext Sample (first 32 bytes)

01 7b 22 50 6c 61 69 6e
74 65 78 74 4d 65 74 61
44 61 74 61 22 3a 7b 22
53 63 61 6c 65 22 3a 7b
...

Each full ciphertext has ~384 KB of pseudo-random bytes. Without the secret key, recovering the list of IDs would require solving Ring-LWE at dimension 8 192 — ~2128 operations.

Total Transferred

384 KBct_A · Bank BB
384 KBct_B · Bank Itaú
384 KBct_C · Bank Bradesco
~1.1 MBTotal sent
Step 05 · Server

Compute intersection under encryption.

The server multiplies the 3 ciphertexts element-wise. Slot by slot, the product is 1 only where ALL three banks have 1 — exactly the intersection.

The Algorithm

// server: three ciphertexts, no key
// (1) ct_A · ct_B under encryption
ctAB := evaluator.Mul(ctA, ctB)
evaluator.Relinearize(ctAB)
// (2) (ct_A · ct_B) · ct_C
ctABC := evaluator.Mul(ctAB, ctC)
evaluator.Relinearize(ctABC)
// result: encrypted binary vector
// 1 = ID is in all 3 lists
// 0 = otherwise

Real Measured Performance

7 msTotal · 3 parties
2Multiplications
1Final ciphertext
0Bytes decrypted
What the server knows now A ciphertext encoding the intersection of the three sets. It has no idea which IDs. It doesn't even know the real size of the banks' lists.
Step 06 · Decryption

Collaborative decryption.

The encrypted result is decrypted collectively (in production, via a threshold quorum). Each 1-bit in the resulting vector reveals an ID in the intersection.

IDs Detected in the Intersection

★ ID#007
★ ID#023
★ ID#089
★ ID#142
★ ID#200

What Each Bank Receives

ONLY these 5 IDs. The other 5 IDs from each list stay private — no bank learns the competitors' lists.

The crucial point Banks can now act on the 5 IDs (alerts, blocks, Coaf reporting). Without having seen anything beyond what was needed.
Step 07 · Proof

Mathematical validation.

To prove that the encrypted computation equals the plaintext one, we compare against the expected intersection computed in cleartext.

FHE vs Plaintext

MetricValue
Expected5 IDs
Found5 IDs
Correct5 / 5
False positives0
False negatives0
StatusPERFECT MATCH

Why It Is Exact

EXACTFHE vs cleartext precision

BGV is an exact scheme (no approximation noise like CKKS). For integers within the plaintext modulus, the FHE result is bit-identical to the plaintext.

Step 08 · Adversarial

What a dishonest bank can get.

Suppose one of the banks (or an employee of the neutral server) tries to find out the OTHER banks' lists from the received ciphertext.

Attack Attempts

  • Attempt 1 — Read ciphertext bytes Result: pseudo-random bytes. Nothing extracted.
  • Attempt 2 — Brute force the lists Universe of 256 positions, ~10 active. Combinations: C(256,10) ≈ 1017. Infeasible; and would still need decryption to verify.
  • Attempt 3 — Recover sk from pk Solve Ring-LWE at dimension 8 192. Best known attack: ~2128 operations. Infeasible on classical or foreseeable quantum hardware.

What leaves the system

The ONLY information that legitimately leaves is the result decrypted by the threshold quorum: the intersection. That's it.

The regulatory point For LGPD and Coaf, the difference between "we handle it carefully" and "mathematically impossible to view" is the difference between residual risk and zero risk.
Step 09 · Summary

What just happened.

In under 1 second total, three banks discovered shared fraudsters without any of them seeing the others' lists.

The Complete Flow

  1. Three banks generated private lists of flagged IDs
  2. Each bank locally encrypted its list under BGV
  3. Only the ciphertexts were sent to the neutral server
  4. The server multiplied the 3 ciphertexts under encryption (7 ms)
  5. Collaborative decryption revealed only the intersection
  6. Each bank discovered the 5 shared fraudsters

Real Numbers From This Run

3 msEncryption per bank
7 msIntersection under encryption
384 KBPer ciphertext
EXACTPrecision
This primitive serves 6 eBooks Banks (AML), Pharmacies (controlled substances), Health plans (claims), Insurers (fraud), E-commerce (chargeback), Government (inter-agency linkage). Same math, different industries.
How this scales to real productionThis demo uses a 256-ID universe represented as a binary characteristic vector — fits in 8 192 slots. For 10M+ IDs per bank (realistic Brazilian scale), a raw characteristic vector would be infeasible: it would require 10M slots. In real production, PSI uses hashing: Bloom filters, Cuckoo hashing or OPRF-based PSI (Chase-Miao, SoK 2020). The "encrypted AND via multiplication" primitive stays the same; only the input representation changes. The pattern is used by real bank-partnership systems and by Google Password Checkup.
PT EN