At a glance#
| Number | Value | What it drives |
|---|---|---|
| Aadhaar numbers | 1.45B issued (~1.35B live) | the gallery every new enrolment is searched against |
| Authentications | 90M+ / day | ~1,040 / s on average |
| Peak | ~3,000 auth / s (3×) | modest volume: latency and security are the hard parts |
| Auth latency | < 200 ms | network 50 · crypto 30 · lookup 20 · match 80 · overhead 20 |
| Dedup work | 600+ trillion matches / day (2014, ~1M enrolments/day) | async, 3 ABIS vendors, minutes to days |
| Brute force | ~3,170 years per day of enrolment at 100K matches/s | biometric indexing + massively parallel matching |
| Storage | ~257 KB templates / person ≈ 360 TB | 7 PB raw images, ~20 PB with 3× replication |
| Accuracy | FNIR 0.035 % · FPIR 0.057 % (2012) | fingerprint + iris fusion |
Architecture#
DiagramAadhaar on one page: enrolment goes async to dedup, authentication goes sync to a 1:1 match
%% title: Aadhaar on one page: enrolment goes async to dedup, authentication goes sync to a 1:1 match
flowchart TD
RES@{ icon: "sd:user", label: "Resident<br/>village camp or service point" } --> ES@{ icon: "sd:laptop", label: "Enrolment station · offline-first<br/>10 fingers · 2 iris · photo" }
RES --> SPD@{ icon: "sd:fingerprint", label: "Certified capture device<br/>encrypts PID with UIDAI public key · signs" }
ES ==>|"encrypted 3–5 MB packet · HMAC<br/>SFTP or encrypted USB"| RG@{ icon: "sd:building", label: "Registrar<br/>checks structure + signatures" }
RG ==> DD["CIDR dedup orchestrator<br/>decrypt · extract templates · async"]:::core
DD ==> AB["3 ABIS vendors<br/>1:N on the full gallery · 2-of-3 vote"]:::service
SPD --> AUA@{ icon: "sd:building", label: "AUA · bank, telecom, scheme<br/>600+ entities · rate-limited" }
AUA ==>|"encrypted PID · txn ID · consent"| ASA@{ icon: "sd:network", label: "ASA · ~35 licensed<br/>dedicated leased lines" }
ASA ==> AU["CIDR auth service<br/>1:1 match · < 200 ms · signed yes/no"]:::hot
AU --> HSM@{ icon: "sd:key", label: "HSM<br/>decrypt PID · sign response" }
AU --> DB@{ icon: "sd:database", label: "Resident + template store<br/>2 data centres · encrypted at rest" }
AU -.-> LOG@{ icon: "sd:log", label: "Auth log · written async<br/>history the resident can see" }
AB -->|"unique → new 12-digit number"| DB
class RES client
class ES,SPD edge
class RG,AUA external
class ASA edge
class HSM,DB,LOG store- Two paths, one store: enrolment is asynchronous and may take 3–90 days; authentication is synchronous and has one candidate.
- CIDR is never on the internet; only certified devices encrypt, and only CIDR's HSM can decrypt.
DiagramDeduplication: three independent matchers and a 2-of-3 vote (this design)
%% title: Deduplication: three independent matchers and a 2-of-3 vote (this design)
flowchart TD
CP["CIDR processing<br/>decrypt · templates · cheap demographic check"]:::core --> A1["ABIS 1 · vendor A"]:::service
CP --> A2["ABIS 2 · vendor B"]:::service
CP --> A3["ABIS 3 · vendor C"]:::service
A1 --> CN{"Consensus<br/>fingerprint + iris fusion scores"}
A2 --> CN
A3 --> CN
CN -->|"2 of 3 UNIQUE"| GEN["Generate Aadhaar number<br/>store · print and mail letter"]:::ok
CN -->|"2 of 3 DUPLICATE"| REJ["Reject<br/>points to the existing Aadhaar"]:::bad
CN -->|"no two agree"| MAN@{ icon: "sd:users", label: "Manual adjudication" }
class MAN queue- Aadhaar itself splits load across its three vendors and cross-checks suspected duplicates; this design sends every packet to all three.
Key flows#
DiagramFingerprint authentication at a bank, under 200 ms
%% title: Fingerprint authentication at a bank, under 200 ms sequenceDiagram autonumber participant D as Capture device participant A as AUA (bank) participant S as ASA participant C as CIDR auth participant H as HSM D->>D: capture · encrypt PID with UIDAI public key · sign D->>A: encrypted PID + Aadhaar or VID A->>S: auth request · txn ID · consent S->>C: forward over leased line C->>H: decrypt PID block H-->>C: captured fingerprint C->>C: resolve VID · load template · 1-to-1 match C->>H: sign response C-->>S: y or n · txn ID · timestamp S-->>A: signed yes/no Note over A,C: no PII, no score · audit row written async, off the 200 ms path
DiagramEnrolment: capture offline, deduplicate at CIDR
%% title: Enrolment: capture offline, deduplicate at CIDR
sequenceDiagram
autonumber
participant E as Enrolment station
participant R as Registrar
participant C as CIDR
participant V as ABIS 1–3
E->>E: capture offline · encrypt · HMAC · operator signs
E->>R: packet by SFTP or USB, within 30 days
R->>R: check structure + signatures
R->>C: upload over secure channel
C->>C: decrypt · extract templates
C->>V: search the full gallery, in parallel
V-->>C: UNIQUE / DUPLICATE / REVIEW from each
alt 2 of 3 say UNIQUE
C->>C: generate 12-digit number · store
Note over C: letter with number + QR printed and mailed
else 2 of 3 say DUPLICATE
C-->>R: reject · points to existing Aadhaar
else no two agree
C->>C: manual adjudication
end
Note over E,V: 3–90 days end to endDiagramEdge paths: worn fingerprints, and a replayed capture
%% title: Edge paths: worn fingerprints, and a replayed capture
sequenceDiagram
autonumber
participant D as Device + AUA
participant C as CIDR
D->>C: fingerprint auth, worn prints
C-->>D: n
D->>C: another finger, then iris
alt still no match
D->>C: OTP to registered mobile, or face with liveness
C-->>D: y
end
Note over D,C: genuine residents get a fallback, never a lower global threshold
D->>C: yesterday's signed capture, resent
C-->>D: reject · stale timestamp, txn ID already seen
Note over C: alert: one device, hundreds of residents, far from homeData model#
DiagramCore entities
%% title: Core entities
classDiagram
class Resident {
+aadhaar : 12 digits
+demographics : ~2 KB
+mobile : for OTP
+vids : 16 digits · revocable
+biometricLock : bool
}
class BiometricRecord {
+fingerprints : 10 templates · ~100 KB
+iris : 2 templates · ~100 KB
+photo : ~50 KB
+rawImages : ~5 MB archived
}
class EnrolmentPacket {
+packetId : string
+payload : encrypted · 3–5 MB
+hmac : bytes
+operatorSig : biometric
+gps : coordinates
+deviceId : string
}
class AuaToken {
+auaCode : string
+token : unique per AUA
}
class AuthTransaction {
+txnId : string · replay check
+auaCode : string
+mode : finger / iris / face / OTP / demo
+result : y / n
+at : timestamp
}
EnrolmentPacket "1" --> "0..1" Resident : creates
Resident "1" --> "1" BiometricRecord : has
Resident "1" --> "many" AuaToken : one per AUA
AuaToken "1" --> "many" AuthTransaction : history- Templates are keyed by Aadhaar number, so authentication is one indexed read; ~257 KB per person, ~360 TB in all.
- Per-AUA tokens and VIDs mean two services can't join their records on a shared identifier.
Lifecycle#
DiagramEnrolment packet state machine
%% title: Enrolment packet state machine stateDiagram-v2 [*] --> CAPTURED: encrypted on the laptop CAPTURED --> UPLOADED: sync or USB, within 30 days UPLOADED --> TAMPERED: bad signature or HMAC UPLOADED --> DEDUP_PENDING: queued for ABIS DEDUP_PENDING --> ISSUED: 2 of 3 unique DEDUP_PENDING --> REJECTED: 2 of 3 duplicate DEDUP_PENDING --> MANUAL_REVIEW: no consensus MANUAL_REVIEW --> ISSUED: cleared MANUAL_REVIEW --> REJECTED: duplicate confirmed ISSUED --> [*]: letter mailed REJECTED --> [*] TAMPERED --> [*] class ISSUED ok class REJECTED,TAMPERED bad class MANUAL_REVIEW queue
Decisions#
| Decision | Chose | Over | Because |
|---|---|---|---|
| Two workloads | 1:1 sync auth, 1:N async dedup | One matching path | 200 ms vs minutes to days |
| Dedup search space | Full biometric gallery | Demographic blocking alone | a fraudster picks a new name, DOB, state |
| Matchers | 3 independent ABIS, 2-of-3 vote | One vendor | different algorithms catch different cases; no lock-in |
| What auth returns | Signed yes/no + txn ID | Score or data | nothing to leak, no hill-climbing |
| Where biometrics decrypt | Only in CIDR's HSM | At the AUA or ASA | no hop before CIDR can read or alter it |
| Enrolment connectivity | Offline-first, encrypted packets | Online capture | 640,000+ villages, many without internet |
| Exposing the number | VID + per-AUA tokens | Raw Aadhaar everywhere | services can't correlate a person |
When it breaks#
| Failure | What users see | Contain it with |
|---|---|---|
| Worn or faded fingerprints | first attempt fails | other finger → iris → OTP → face |
| Camp offline for weeks | number comes later, nothing lost | packets stay encrypted, sync or USB within 30 days |
| Re-enrolment under a new name | rejected, linked to first Aadhaar | full-gallery search, 3 ABIS, adjudication |
| Replayed capture or silicone finger | fraud blocked | device key + timestamp + txn ID, liveness, OTP, per-device anomaly alerts |
| DDoS or brute force | that AUA throttled | rate limit per AUA, ASA IP allow-list, no internet exposure |
| ABIS backlog grows | new numbers take longer | async queue, monitored; auth path untouched |
Say it in the interview#
- "Authentication is a lookup, deduplication is a search." 1:1 with a number in 200 ms; 1:N against 1.45B, async.
- Blocking doesn't stop fraud: demographics are chosen by the fraudster, so the biometric search covers the full gallery.
- Three matchers, two must agree: independent algorithms cut errors; disagreement goes to a human.
- Return a bit, not data: signed yes/no, no score, no PII; e-KYC data only with consent.
- Encrypt at the sensor, decrypt in the HSM: every hop in between, even a USB stick, carries ciphertext.
- A fingerprint is an identifier, not a secret: rotate what you can (device keys, factors) and let residents lock biometrics.
- Design for the village: offline-first enrolment and multi-modal fallbacks are what make 1B+ enrolments possible.