Want it harder? Hide the model answer and work each phase in a scratchpad, then reveal and score yourself. Or simply read on: the checkpoints below work either way.
Identity at Billion Scale
Imagine this challenge: You need to uniquely identify 1.4 billion people.
Not just assign them a number — but guarantee that each person appears exactly once in your system. No duplicates. No fakes. Every identity verifiable in under 200 milliseconds.
You'll need to match billions of fingerprints against billions of other fingerprints. Trillions of biometric comparisons. Every single day.
This is Aadhaar — and it's the largest biometric identity system ever built.
Enrollment
- Aadhaar numbers issued: ~1.45 Billion (144.8 crore, UIDAI saturation report, 30 June 2026), about 1.35 billion live after estimated deaths
- Coverage: ~95% of the population (live Aadhaar, June 2026)
- Biometric data: ~10-15 Petabytes (10 fingerprints + 2 iris scans + photo per person; UIDAI's 2014 estimate)
Authentication
- Daily authentications: 90+ Million ("nearly 10 crore" a day per UIDAI, September 2026)
- Monthly authentications: 2.1-2.3 Billion (e.g. 211 crore in May, 231 crore in November 2025)
- Cumulative (to date): 170+ Billion authentications (March 2026)
- e-KYC transactions: ~47 Crore (470 Million)/month (November 2025)
- Face authentication: ~28 Crore (280 Million)/month (November 2025, AI-powered)
Performance
- Authentication latency: < 200ms
- Availability: 99.9%+ (target)
- Entities using the Aadhaar platform: 600+ (UIDAI chairman, September 2026)
Deduplication
- Biometric matches/day: 600+ Trillion (at the ~1M/day enrolment peak, 2014)
- ABIS vendors: 3 (for accuracy and throughput)
- Duplicates caught: 99.965% (FNIR 0.035%, UIDAI's 2012 measurement)
Impact
- DBT savings:
₹3.48 Lakh Crore ($40B) saved by Direct Benefit Transfer, 2009-2024 (an analysis reported in April 2025; it credits Aadhaar authentication with a critical role) - Ghost beneficiaries: Millions eliminated (government estimates)
- Bank accounts linked: Hundreds of millions
This is the system we'll design today — and understand the engineering marvel behind proving "you are you" at planetary scale.
Aadhaar has issued 1.45 billion numbers and answers 90 million+ authentications a day in under 200 ms, while brute-force deduplication would take 3,170 years per day of enrolment. Offline kits encrypt enrolment packets for CIDR, where three ABIS vendors must agree 2 of 3; banks (AUAs) reach CIDR through ASAs and get back only a signed Yes or No. The deep dives cover full-gallery dedup, the 200 ms budget (80 ms for the match), PKI, HSMs, Virtual IDs and face authentication, then village-scale enrolment, fallbacks and defences against replayed or cloned fingerprints.
The Interview Begins#
You're interviewing at a government technology agency. The Chief Architect draws on the whiteboard:
"India's Aadhaar is often cited as the world's most ambitious digital identity project. I want you to design a biometric identity system that can scale to a billion people. Walk me through how you'd approach it."
Build an identity system that can:
Requirements
- Enroll 1+ billion residents with biometrics
- Guarantee uniqueness (no duplicates in the system)
- Authenticate identity in real-time (< 500ms)
- Handle 100+ million authentications per day
- Work across 640,000 villages with unreliable connectivity
- Protect biometric data with highest security
- Provide e-KYC (Know Your Customer) service
- Support multiple authentication modes (fingerprint, iris, OTP)
- 99.9%+ availability
Constraint: This is a government project with vendor neutrality
"The uniqueness guarantee is the hardest part. You need to prove that each of 1.4 billion people appears exactly once. That's never been done before at this scale."
Phase 1: Requirements Clarification#
"Let me understand the specific challenges before designing."
Your Questions#
"First, what biometric modalities are we capturing? And what's the expected quality given enrollment happens in remote villages?"
"10 fingerprints, 2 iris scans, and a photograph. Quality will vary — many manual laborers have worn fingerprints, elderly may have faded prints, some people lack fingers. The system must handle all cases."
"For uniqueness, what's the acceptable error rate? False accepts (enrolling duplicates) vs false rejects (wrongly rejecting unique people)?"
"This is critical. A false accept means someone gets two identities — they can claim benefits twice. A false reject means a legitimate person can't get enrolled. Both are bad, but false accepts are worse for a welfare system."
"What about authentication? Is it 1:1 matching (verify this person is who they claim) or 1:N (find this person in the database)?"
"Authentication is always 1:1 — they provide their Aadhaar number plus biometric, we verify it matches the stored data. Deduplication during enrollment is 1:N — we search the entire database to ensure they're not already enrolled."
"What infrastructure can we assume in remote areas?"
"Minimal. Many areas have no internet, unreliable power, extreme temperatures. Enrollment must work offline. Authentication needs connectivity but should degrade gracefully."
Requirements Summary#
Functional Requirements:
1. Enrollment
- Capture demographics (name, address, DOB, gender)
- Capture biometrics (10 fingerprints, 2 iris, 1 photo)
- Verify supporting documents (proof of identity/address)
- Perform deduplication (
1:Nmatch against entire database) - Generate unique 12-digit Aadhaar number
- Print and mail physical Aadhaar letter
2. Authentication
- Demographic authentication (name/address matching)
- Biometric authentication (fingerprint, iris, face)
- OTP authentication (via registered mobile)
- Multi-factor authentication (combinations)
- Return only Yes/No (no PII in response)
3. e-KYC (Know Your Customer)
- Return verified identity data after authentication
- Digitally signed response
- Replace paper-based KYC for banks, telecom, etc.
4. Update
- Demographics update (address, phone, etc.)
- Biometrics update (for degraded prints)
- Document-based or operator-assisted updates
5. Privacy features
- Virtual ID (16-digit temporary alias for Aadhaar)
- Masked Aadhaar (partially hidden number)
- Authentication history (user can see who queried)
Non-Functional Requirements:
Scale
- 1.4 billion enrolled residents
- 90+ million authentications/day
- Peak: ~3,000 authentications/second
- 15+ petabytes of biometric data
Latency
- Authentication: < 200ms (1:1 match)
- Deduplication: minutes (
1:Nagainst billions)
Accuracy
- False Positive Identification Rate (FPIR): < 0.06% (UIDAI measured 0.057% in 2012)
- False Negative Identification Rate (FNIR): < 0.035% (UIDAI measured 0.035%)
Availability
- 99.9%+ uptime
- Geo-distributed for disaster recovery
Security
- 2048-bit PKI encryption
- Data encrypted at rest and in transit
- HSM for key management
- No biometric data leaves CIDR
Phase 2: Back of the Envelope Estimation#
"Let me work through the numbers to understand the computational challenge."
Before the numbers: 1:1 versus 1:N
Authenticating someone compares their fingerprint with the one record their number points to. Enrolling someone must prove they are not already among a billion people with ten fingerprints each. At a million enrolments a day and 100,000 matches a second, how long would brute-force deduplication take? What does that tell you about the design?
One strong answer
- Templates on file: 1B people × 10 fingers = 10 billion.
- Comparisons: 1M new people × 10B templates = 10^16 a day.
- Time: 10^16 ÷ 10^5 per second = 10^11 seconds, about 3,170 years for each day of enrolment.
So two very different paths: authentication is 1:1, synchronous and has to finish in milliseconds, while deduplication is 1:N, runs asynchronously (minutes to days are fine) and needs biometric indexing, multi-modal fusion and massively parallel matching hardware to be possible at all.
The Deduplication Challenge#
To guarantee uniqueness, every new enrollment must be compared against EVERY existing record — for 1 billion people with 10 fingerprints each:
- Fingerprint comparisons for new enrollment: 1,000,000,000 people × 10 fingers = 10 billion templates
- If we enroll 1 million new people per day: 1,000,000 × 10 billion = 10,000,000,000,000,000 comparisons/day = 10 quadrillion matches/day!
- At traditional matching speed (100,000 matches/sec): 10^16 / 10^5 = 10^11 seconds = 3,170 years per day of enrollment!
This is mathematically impossible with brute force.
The Solution: Multi-Modal + Multi-ABIS#
1. Demographic pre-filter
Before biometric matching, filter by:
- Name phonetics
- Date of birth
- Gender
- Geographic region
This can cut the search space by 99%+, but only as a cheap first check: a fraudster can change name, DOB or region, so Aadhaar's ABIS still searches biometrics against the full gallery (see Deep Dive 1)
2. Multi-modal biometrics
Using fingerprint + iris together:
- Fingerprint alone: highly distinctive, but worn or faded prints raise error rates
- Iris alone: very low false-match rates, unaffected by manual labour
- Combined: independent errors multiply, so the fused error rate is far lower than either alone
The combination allows lower thresholds per modality
3. Hierarchical matching
- First: Fast, approximate match (GPU-accelerated)
- If potential match: Detailed matching
- If still ambiguous: Human adjudication
4. Three ABIS vendors
- Aadhaar shares the deduplication load across three vendors, and a suspected duplicate from one is sent to another ABIS to verify
- This design goes further: every enrollment goes to all three, and 2 of 3 must agree
- Different algorithms catch different edge cases
Storage Estimation#
- Per person · 10 fingerprints: 10 × 10KB template = ~100KB
- Per person · 2 iris scans: 2 × 50KB template = ~100KB
- Per person · 1 photograph: ~50KB
- Per person · Demographics: ~2KB
- Per person · Metadata: ~5KB
- Total per person: ~257KB
- For 1.4 billion people: 1.4B × 257KB = 360 TB (templates only)
- Raw biometric images (archived): per person ~5MB (high-res captures); total 1.4B × 5MB = 7 PB
- With replication (3x): ~20 PB total storage
Authentication Traffic#
Traffic
- Daily authentications: 90,000,000
- Seconds per day: 86,400
- Average TPS: ~1,040 auth/second
- Peak multiplier: 3x
- Peak TPS: ~3,000 auth/second
Each authentication requires
- Decrypt request (PKI)
- Lookup Aadhaar record
- Biometric 1:1 match
- Sign response
Time budget: 200ms total
- Network: 50ms
- Crypto: 30ms
- Lookup: 20ms
- Match: 80ms
- Overhead: 20ms
Requirements and estimation
Q1/3 Why can authentication finish in under 200ms while deduplication takes minutes or longer?
Answer & explanation
Answer: C
The resident supplies their Aadhaar number, so authentication compares against one stored template. Enrollment has no number yet and must search the whole database. Biometric authentication is still a real match; what makes it fast is having only one candidate.
Q2/3 Using the estimate's 90 million authentications a day and a 3x peak multiplier, what peak rate should CIDR handle?
Answer & explanation
Answer: D
90M / 86,400 s is about 1,040 per second on average; 3x that is about 3,000 per second at peak. 1,000 per second is the average, so sizing to it would leave no room for the daily peak.
Q3/3 For a welfare system, the interviewer says false accepts are worse than false rejects. Why?
Answer & explanation
Answer: A
A false accept enrolls a duplicate, which is exactly the fraud the uniqueness guarantee exists to stop. Blocking a genuine resident is the false reject, which is bad but can be handled with review and re-enrollment.
Phase 3: High-Level Architecture#
"Aadhaar's architecture follows four key principles: openness, linear scalability, strong security, and vendor neutrality."
Sketch it first: who may touch a fingerprint?
Banks, telecoms and government schemes all need to check identities. Sketch who talks to whom for enrolment and for authentication. Should a bank ever receive a stored fingerprint?
One strong answer
- Enrolment: an offline-capable client captures and encrypts everything at capture time, a registrar uploads the packets, and the central repository (CIDR, two active-active data centres never exposed to the internet) runs deduplication through independent ABIS vendors.
- Authentication: the requesting agency (AUA, say a bank) sends the request through a licensed service agency (ASA) over dedicated lines to CIDR.
- No, never. The bank forwards a biometric captured and encrypted on the device with CIDR's public key. Only CIDR can decrypt it, CIDR does the 1:1 match, and it answers with a signed yes/no and a transaction ID. Identity details come back only for e-KYC, with the resident's consent.
The Aadhaar Ecosystem#
%% title: Aadhaar architecture — enrollment ecosystem
flowchart TD
RES@{ icon: "sd:user", label: "Resident (Village)" } --> EA@{ icon: "sd:building", label: "Enrollment Agency" }
EA --> RG@{ icon: "sd:building", label: "Registrar (State Govt)" }
EA ==>|"Encrypted packet"| CIDR
RG -->|"Verification"| CIDR
subgraph CIDR["CIDR (Central DB) · deduplication"]
direction TB
A1["ABIS 1"]:::core
A2["ABIS 2"]:::core
A3["ABIS 3"]:::core
CON{"3-way consensus"}:::hot
A1 & A2 & A3 --> CON
end
class RES client
class EA,RG service%% title: Aadhaar architecture — authentication ecosystem
flowchart TD
RES2@{ icon: "sd:user", label: "Resident (at bank, telecom)" } --> SPD@{ icon: "sd:fingerprint", label: "Service Point Device" }
SPD --> AUA@{ icon: "sd:building", label: "AUA (Auth User Agency)" }
AUA ==>|"Encrypted PID"| ASA["ASA (Auth Service Agency)"]:::edge
ASA ==>|"Secure leased line"| CID["CIDR (1:1 matching)<br/>Returns: Yes/No<br/>(no PII returned)"]:::core
class RES2 client
class SPD edge
class AUA externalKey Components#
1. Enrollment client
- Runs on laptops in villages
- Captures biometrics (certified devices only)
- Works OFFLINE (syncs when connected)
- Encrypts everything at capture time
- Operator + Supervisor biometric signatures
2. Registrar
- State governments, banks, oil companies
- Responsible for enrollment agencies
- First-level quality checks
- Uploads packets to CIDR
3. CIDR (Central Identities Data Repository)
- The "crown jewels" — all biometric data
- Two data centers (Hebbal, Bengaluru + Manesar, Gurugram)
- Active-active configuration (in this design)
- NEVER exposed to internet directly
- Only UIDAI has access
4. ABIS (Automated Biometric Identification System)
- Three independent vendors (TCS+Neurotechnology, etc.)
- Aadhaar splits the work among them by throughput; suspected duplicates are cross-checked by a second ABIS
- This design: each runs complete deduplication, with a consensus decision
- Vendor-neutral API integration
5. AUA (Authentication User Agency)
- Banks, telecom, insurance companies
- Licensed to use authentication
- Must follow UIDAI security guidelines
- Audited regularly
6. ASA (Authentication Service Agency)
- Secure network intermediary
- Connects AUAs to CIDR
- Dedicated leased lines (not internet)
- About 35 licensed ASAs in India (2026)
Data Flow: Enrollment#
UIDAI publishes the outline: an offline-capable client, packets encrypted at capture with an HMAC, uploaded by SFTP, deduplicated at CIDR. The hop-by-hop steps below are a likely design.
%% title: Enrollment flow
flowchart TD
RES@{ icon: "sd:user", label: "Resident visits enrollment center with documents" }
subgraph ST1["Step 1 · CAPTURE"]
ES@{ icon: "sd:laptop", label: "Enrollment Station<br/>(Offline-capable laptop)" }
end
subgraph ST2["Step 2 · UPLOAD"]
RB["Registrar Backend<br/>validates structure + signatures"]:::service
RQ@{ icon: "sd:queue", label: "Queue for CIDR upload" }
RB --> RQ
end
subgraph ST3["Step 3 · DEDUPLICATION · 3-90 days"]
CP["CIDR Processing<br/>decrypt · extract templates<br/>demographic pre-filter<br/>send to all ABIS in parallel"]:::core
B1["ABIS 1"]:::service
B2["ABIS 2"]:::service
B3["ABIS 3"]:::service
CN{"Consensus:<br/>2 of 3 must agree"}:::hot
REJ(["Reject · points to existing Aadhaar"]):::bad
MA["Manual adjudication"]:::queue
GEN["Generate Aadhaar number"]:::ok
DB@{ icon: "sd:database", label: "Store in database" }
PQ@{ icon: "sd:printer", label: "Queue letter for printing" }
CP --> B1 & B2 & B3
B1 & B2 & B3 --> CN
CN -->|"DUPLICATE"| REJ
CN -->|"No consensus"| MA
MA -.->|"cleared"| GEN
CN ==>|"UNIQUE"| GEN
GEN --> DB --> PQ
end
subgraph ST4["Step 4 · DELIVERY"]
LET@{ icon: "sd:mail", label: "Aadhaar Letter printed and mailed<br/>12-digit Aadhaar number + QR code" }
end
RES --> ES
ES ==>|"Encrypted enrollment packet (3-5 MB)<br/>HMAC for tamper detection<br/>via SFTP or encrypted USB"| RB
RQ ==>|"secure channel"| CP
PQ --> LET
class RES client
class ES service
class RQ,PQ queue
class DB store
class LET okStep 1: CAPTURE — Enrollment Station (offline-capable laptop)
- Operator logs in with biometric
- Captures resident's demographics
- Scans proof documents (Ration card, Voter ID, etc.)
- Captures 10 fingerprints (slaps + thumbs)
- Captures 2 iris scans
- Captures photograph
- Resident reviews and confirms
- Operator signs packet biometrically
- Supervisor approves (for exceptions)
Output: Encrypted enrollment packet (3-5 MB), contains HMAC for tamper detection.
Step 2: UPLOAD — Registrar Backend
- Receives packets via SFTP or encrypted USB
- Validates packet structure and signatures
- Queues for CIDR upload
- Uploads via secure channel to CIDR
Step 3: DEDUPLICATION — CIDR Processing
- Decrypt packet (only CIDR can decrypt)
- Extract biometric templates
- Demographic pre-filter (reduce search space)
- Send to ABIS 1, ABIS 2, ABIS 3 in parallel
- Each ABIS returns: UNIQUE / DUPLICATE / MANUAL_REVIEW
- Consensus: 2 of 3 must agree
- If DUPLICATE: Reject (linked to the existing Aadhaar); if no consensus: manual adjudication
- If UNIQUE: Generate Aadhaar number
- Store in database
- Queue letter for printing
Timeline: 3-90 days (depending on duplicates)
(This flow sends every packet to all three ABIS for a 2-of-3 vote. Aadhaar itself splits the load across its three vendors and sends suspected duplicates and poor-quality enrollments to more than one ABIS.)
Step 4: DELIVERY — Aadhaar Letter printed and mailed to resident's address. Contains: 12-digit Aadhaar number + QR code.
High-level architecture
Q1/3 Enrollment packets travel from village laptops through registrars, sometimes on USB drives. Why are they encrypted at capture time?
Answer & explanation
Answer: B
Encrypting on the device means every hop after capture, including registrars and USB transport, carries data only CIDR can decrypt, and the HMAC exposes tampering. Registrars validate packet structure and signatures; they do not need to read the biometrics.
Q2/3 A bank authenticates a customer's fingerprint through Aadhaar. What does the bank get back?
Answer & explanation
Answer: D
CIDR does the 1:1 match itself and returns only Yes/No. Returning templates or scores would leak data through every AUA. Demographic data is returned only through e-KYC, with consent, not in plain authentication.
Q3/3 Why do AUAs reach CIDR through ASAs over leased lines instead of calling CIDR directly?
Answer & explanation
Answer: A
ASAs are a small set of licensed network intermediaries on dedicated lines, which keeps the crown-jewel database off the public internet. Matching happens only inside CIDR; ASAs just carry the encrypted request.
Phase 4: Deep Dives#
Deep Dive 1: Biometric Deduplication at Billion Scale#
Week 1 concepts: Partitioning, sharding. Week 3 concepts: Async processing.
"Deduplication is the hardest problem in Aadhaar. You must compare each new person against 1.4 billion existing records."
Why not just filter by name and birth date?
Splitting candidates by gender, age band, state and a phonetic name hash would shrink each search about 72,000-fold. Why is that not enough to guarantee that nobody enrols twice?
One strong answer
Because the person trying to enrol twice chooses their demographics. Someone after a second identity simply gives a different name, birth date or state, and a demographic filter would never compare them with their first record. Blocking only catches careless duplicates.
So the biometric search has to cover the full gallery, made feasible with biometric indexing and massively parallel matching. Fusing fingerprints with iris multiplies independent errors down; three independent ABIS vendors must reach a 2-of-3 consensus; and borderline cases go to human adjudication.
Without optimization:
- New enrollment: 1 person
- Existing database: 1,400,000,000 people
- Fingers per person: 10
- Iris per person: 2
- Fingerprint comparisons: 10 (new) × 10 (existing) × 1.4B = 140 billion comparisons
- At 1 million matches/second: 140,000 seconds = about 1.6 days for one enrollment
- For 1 million enrollments/day: 1.4 × 10^17 comparisons, or about 4,400 years of compute per day of enrollment
This is impossible. We need smarter approaches.
How Aadhaar Makes It Tractable:
DEDUPLICATION OPTIMIZATION STRATEGIES
LAYER 1: DEMOGRAPHIC BLOCKING — a common textbook optimisation, with a big caveat: it only catches careless duplicates, because a fraudster can simply change name, DOB or state. Aadhaar's ABIS therefore searches biometrically against the full gallery (~1M enrollments/day × ~600M records ≈ 600 trillion matches/day at peak) and relies on biometric indexing plus massive parallel hardware. As a textbook blocking scheme, partition by:
- Gender (2 partitions)
- Age range (10-year buckets = 10 partitions)
- State (36 partitions)
- Name phonetic hash (100 partitions)
Effective reduction: 2 × 10 × 36 × 100 = 72,000x smaller search. 1.4B / 72,000 = 19,444 candidate matches per enrollment (vs 1.4 billion without blocking).
LAYER 2: MULTI-MODAL FUSION — Fingerprint (10 fingers) + Iris (2 eyes) combined:
- Fingerprint score (0-100) × weight + Iris score (0-100) × weight = Final fusion score
Using both modalities:
- Handles worn fingerprints (use iris)
- Handles cataracts (use fingerprint)
- Much higher accuracy than either alone
LAYER 3: MULTI-ABIS CROSS-CHECK — Aadhaar splits the work across three independent vendors and sends a suspected duplicate to a second ABIS to verify. This design goes further and asks all three for a 2-of-3 vote:
%% title: Three-way ABIS consensus
flowchart TD
A1["ABIS 1 (Vendor A)"]:::service
A2["ABIS 2 (Vendor B)"]:::service
A3["ABIS 3 (Vendor C)"]:::service
CE{"Consensus Engine"}:::core
A1 & A2 & A3 --> CE
CE -->|"3/3 UNIQUE"| ACC(["Accept enrollment"]):::ok
CE -->|"3/3 DUPLICATE"| REJ(["Reject enrollment"]):::bad
CE -->|"2/3 agree"| MAJ(["Follow majority"]):::service
CE -->|"Mixed/uncertain"| MAN(["Manual adjudication"]):::queueWhy three vendors?
- Different algorithms catch different edge cases
- No single vendor lock-in
- Higher accuracy through consensus
- Continuous quality competition
# deduplication/abis_orchestrator.py
"""
ABIS (Automated Biometric Identification System) Orchestration
Aadhaar uses three independent ABIS vendors for deduplication.
This provides redundancy, accuracy, and vendor neutrality.
"""
from dataclasses import dataclass
from typing import List, Optional, Tuple
from enum import Enum
import asyncio
class DeduplicationResult(Enum):
UNIQUE = "unique" # No duplicates found
DUPLICATE = "duplicate" # Duplicate found
MANUAL_REVIEW = "review" # Uncertain, needs human review
@dataclass
class ABISMatch:
candidate_aadhaar: str
fingerprint_score: float # 0-100
iris_score: float # 0-100
fusion_score: float # Combined score
confidence: str # HIGH, MEDIUM, LOW
@dataclass
class ABISResponse:
vendor_id: str
result: DeduplicationResult
matches: List[ABISMatch]
processing_time_ms: int
class ABISOrchestrator:
"""
Orchestrates deduplication across three ABIS vendors.
This design's multi-ABIS approach (Aadhaar itself shares the load
across vendors and cross-checks suspected duplicates):
1. Send enrollment to all three vendors in parallel
2. Each vendor searches against their copy of the database
3. Consensus determines final result
"""
def __init__(
self,
abis_clients: List, # Three ABIS vendor clients
demographic_filter,
manual_review_queue
):
self.abis_clients = abis_clients
self.demo_filter = demographic_filter
self.review_queue = manual_review_queue
# Thresholds for decision
self.duplicate_threshold = 80 # Fusion score > 80 = duplicate
self.unique_threshold = 30 # Fusion score < 30 = unique
async def deduplicate(
self,
enrollment_packet: dict
) -> Tuple[DeduplicationResult, Optional[str]]:
"""
Main deduplication flow.
Returns:
(result, duplicate_aadhaar if found)
"""
# Step 1: Demographic blocking to reduce search space
candidate_pool = await self.demo_filter.get_candidates(
gender=enrollment_packet['gender'],
dob=enrollment_packet['dob'],
state=enrollment_packet['state'],
name_phonetic=enrollment_packet['name_phonetic']
)
# Log the reduction achieved
reduction_ratio = 1_400_000_000 / len(candidate_pool)
print(f"Demographic blocking: {len(candidate_pool):,} candidates")
print(f"Search space reduced by {reduction_ratio:,.0f}x")
# Step 2: Send to all three ABIS in parallel
abis_tasks = [
client.search(
fingerprints=enrollment_packet['fingerprints'],
irises=enrollment_packet['irises'],
candidate_pool=candidate_pool
)
for client in self.abis_clients
]
responses: List[ABISResponse] = await asyncio.gather(*abis_tasks)
# Step 3: Consensus decision
return self._consensus_decision(responses)
def _consensus_decision(
self,
responses: List[ABISResponse]
) -> Tuple[DeduplicationResult, Optional[str]]:
"""
Three-way consensus logic.
This design requires 2/3 agreement for automated decision.
Mixed results go to manual adjudication.
"""
unique_count = sum(
1 for r in responses if r.result == DeduplicationResult.UNIQUE
)
duplicate_count = sum(
1 for r in responses if r.result == DeduplicationResult.DUPLICATE
)
# Case 1: All three agree UNIQUE
if unique_count == 3:
return (DeduplicationResult.UNIQUE, None)
# Case 2: All three agree DUPLICATE
if duplicate_count == 3:
# Find the matching Aadhaar (should be same across all)
duplicate_aadhaar = self._find_common_match(responses)
return (DeduplicationResult.DUPLICATE, duplicate_aadhaar)
# Case 3: 2/3 agree UNIQUE
if unique_count >= 2:
return (DeduplicationResult.UNIQUE, None)
# Case 4: 2/3 agree DUPLICATE
if duplicate_count >= 2:
duplicate_aadhaar = self._find_common_match(responses)
return (DeduplicationResult.DUPLICATE, duplicate_aadhaar)
# Case 5: No consensus — manual review
# Queue for human adjudicator
return (DeduplicationResult.MANUAL_REVIEW, None)
def _find_common_match(
self,
responses: List[ABISResponse]
) -> Optional[str]:
"""
Find the Aadhaar number that multiple ABIS agree is a duplicate.
"""
# Collect all top matches
match_counts = {}
for response in responses:
if response.matches:
top_match = response.matches[0]
aadhaar = top_match.candidate_aadhaar
match_counts[aadhaar] = match_counts.get(aadhaar, 0) + 1
# Return the one with most agreement
if match_counts:
return max(match_counts, key=match_counts.get)
return None
class DemographicBlockingFilter:
"""
Pre-filters the biometric search space using demographics.
This is crucial for making billion-scale deduplication tractable.
"""
def __init__(self, database):
self.db = database
async def get_candidates(
self,
gender: str,
dob: str,
state: str,
name_phonetic: str
) -> List[str]:
"""
Get candidate Aadhaar numbers that match demographic criteria.
Reduces 1.4 billion to ~20,000-50,000 candidates.
"""
# Parse DOB to get age range
birth_year = int(dob[:4])
age_range_start = birth_year - 5
age_range_end = birth_year + 5
# Query with demographic filters
candidates = await self.db.query("""
SELECT aadhaar_number
FROM residents
WHERE gender = ?
AND birth_year BETWEEN ? AND ?
AND state_code = ?
AND name_phonetic_hash = ?
""", [gender, age_range_start, age_range_end,
state, self._phonetic_hash(name_phonetic)])
return [row['aadhaar_number'] for row in candidates]
def _phonetic_hash(self, name: str) -> str:
"""
Generate phonetic hash for name matching.
Handles spelling variations:
"Rahul" and "Rahool" → same hash
"Priya" and "Priyaa" → same hash
"""
# Simplified phonetic algorithm (a real system would use a Soundex-style one)
# Remove vowels, normalize consonants
consonants = ''.join(c for c in name.upper() if c not in 'AEIOU')
return consonants[:4].ljust(4, '0')Deep Dive 2: Authentication in 200 Milliseconds#
Week 2 concepts: Timeouts, latency budgets. Week 4 concepts: Caching.
"Authentication must verify identity in under 200ms. This is 1:1 matching — much simpler than deduplication, but still demanding at scale."
Spend the 200 milliseconds
List the steps an authentication request goes through between the bank and the repository, and give each a share of the 200 ms budget. Which step gets the largest slice?
One strong answer
- Network, agency → service agency → CIDR and back: ~50 ms.
- Cryptography, decrypting the biometric block, verifying the signature, signing the response: ~30 ms.
- Lookup of the record and its stored template: ~20 ms.
- The 1:1 biometric match: ~80 ms, the largest slice.
- Overhead: ~20 ms.
With the match taking the biggest share, everything around it has to be predictable: templates indexed by number, crypto in HSMs, no fan-out to other services, and strict per-hop timeouts.
Total budget: 200ms
- Network (AUA → ASA → CIDR → ASA → AUA): 50ms
- AUA to ASA: 15ms
- ASA to CIDR: 10ms
- CIDR to ASA: 10ms
- ASA to AUA: 15ms
- Cryptographic operations: 30ms
- Decrypt PID block (at CIDR): 10ms
- Signature verification: 5ms
- Sign response: 15ms
- Database lookup: 20ms
- Find Aadhaar record: 10ms
- Load biometric template: 10ms
- Biometric 1:1 matching: 80ms
- Fingerprint match: 40ms
- Iris match (if used): 40ms
- Processing overhead: 20ms
- Total: 200ms
A bank (the AUA) sends the encrypted PID block via an ASA to CIDR (25 ms). CIDR's HSM decrypts it and checks the signature (15 ms), the record and template load (20 ms) and the 1:1 match takes the largest slice (80 ms); signing, the trip back and overhead (15 + 25 + 20 ms) complete the 200 ms. Only y or n returns, never biometric or personal data, and after an n the bank falls back to another finger, iris, OTP or face.
# authentication/auth_service.py
"""
Aadhaar Authentication Service
Processes 90+ million authentications per day with <200ms latency.
Returns only Yes/No — never exposes biometric or demographic data.
"""
from dataclasses import dataclass
from typing import Optional
from enum import Enum
from datetime import datetime, timedelta
import asyncio
class AuthMode(Enum):
DEMOGRAPHIC = "demo" # Name/address matching
OTP = "otp" # One-time password
FINGERPRINT = "fmr" # Fingerprint biometric
IRIS = "iir" # Iris biometric
FACE = "fid" # Face biometric
MULTI_FACTOR = "mf" # Combination
@dataclass
class AuthRequest:
aadhaar_number: str # Or VID (Virtual ID)
auth_mode: AuthMode
pid_block: bytes # Encrypted biometric/OTP
aua_code: str # Authentication User Agency
timestamp: datetime
transaction_id: str
consent: bool # Must be True
@dataclass
class AuthResponse:
transaction_id: str
status: str # "y" (yes) or "n" (no)
error_code: Optional[str] # If status is "n"
auth_token: Optional[str] # Unique token for this auth
timestamp: datetime
# NOTE: No PII is ever returned!
class AadhaarAuthService:
"""
Core authentication service running in CIDR.
Security principles:
1. All requests encrypted with 2048-bit PKI
2. Biometric data decrypted only inside CIDR
3. Response is only Yes/No (no data leakage)
4. All transactions logged for audit
"""
def __init__(
self,
biometric_db,
biometric_matcher,
otp_service,
hsm_client, # Hardware Security Module
audit_logger
):
self.db = biometric_db
self.matcher = biometric_matcher
self.otp = otp_service
self.hsm = hsm_client
self.audit = audit_logger
# Performance tuning
self.template_cache = {} # LRU cache for hot Aadhaars
self.cache_ttl = 300 # 5 minutes
async def authenticate(
self,
request: AuthRequest
) -> AuthResponse:
"""
Main authentication flow.
Must complete in <200ms.
"""
start_time = datetime.utcnow()
try:
# Step 1: Validate request format
self._validate_request(request)
# Step 2: Decrypt PID block using HSM
# Only CIDR's HSM can decrypt
pid_data = await self.hsm.decrypt_pid(request.pid_block)
# Step 3: Resolve Aadhaar number (handle VID)
aadhaar = await self._resolve_aadhaar(request.aadhaar_number)
# Step 4: Load resident's template (with caching)
resident_template = await self._load_template(aadhaar)
# Step 5: Perform matching based on auth mode
match_result = await self._perform_match(
mode=request.auth_mode,
pid_data=pid_data,
stored_template=resident_template
)
# Step 6: Generate response
response = AuthResponse(
transaction_id=request.transaction_id,
status="y" if match_result.success else "n",
error_code=match_result.error_code,
auth_token=self._generate_token(aadhaar, request.aua_code),
timestamp=datetime.utcnow()
)
# Step 7: Audit logging (async, don't wait)
asyncio.create_task(self.audit.log(
aadhaar=aadhaar,
aua_code=request.aua_code,
auth_mode=request.auth_mode,
result=response.status,
latency_ms=(datetime.utcnow() - start_time).total_seconds() * 1000
))
return response
except ValidationError as e:
return self._error_response(request, str(e))
except Exception as e:
# Never expose internal errors
return self._error_response(request, "INTERNAL_ERROR")
async def _load_template(self, aadhaar: str) -> dict:
"""
Load biometric template with caching.
Hot Aadhaars (frequently authenticated) are cached.
"""
# Check cache first
if aadhaar in self.template_cache:
cached = self.template_cache[aadhaar]
if cached['expires'] > datetime.utcnow():
return cached['template']
# Cache miss — load from database
template = await self.db.get_template(aadhaar)
# Cache for hot Aadhaars
self.template_cache[aadhaar] = {
'template': template,
'expires': datetime.utcnow() + timedelta(seconds=self.cache_ttl)
}
return template
async def _perform_match(
self,
mode: AuthMode,
pid_data: dict,
stored_template: dict
) -> MatchResult:
"""
Perform biometric/demographic/OTP matching.
"""
if mode == AuthMode.FINGERPRINT:
# 1:1 fingerprint matching
return await self.matcher.match_fingerprint(
captured=pid_data['fingerprint'],
stored=stored_template['fingerprints'],
finger_position=pid_data.get('position', 'ANY')
)
elif mode == AuthMode.IRIS:
# 1:1 iris matching
return await self.matcher.match_iris(
captured=pid_data['iris'],
stored=stored_template['irises']
)
elif mode == AuthMode.FACE:
# AI-powered face matching
return await self.matcher.match_face(
captured=pid_data['face_image'],
stored=stored_template['photo'],
liveness_check=pid_data.get('liveness_data')
)
elif mode == AuthMode.OTP:
# Verify OTP sent to registered mobile
return await self.otp.verify(
aadhaar=stored_template['aadhaar'],
submitted_otp=pid_data['otp']
)
elif mode == AuthMode.DEMOGRAPHIC:
# Fuzzy matching on name/address
return self._demographic_match(
submitted=pid_data['demographics'],
stored=stored_template['demographics']
)
def _demographic_match(self, submitted: dict, stored: dict) -> MatchResult:
"""
Fuzzy matching for name and address.
Handles variations like:
- "Raj Kumar" vs "Rajkumar"
- "Bangalore" vs "Bengaluru"
- Hindi transliterations
"""
name_score = self._fuzzy_name_match(
submitted.get('name', ''),
stored['name']
)
address_score = self._fuzzy_address_match(
submitted.get('address', ''),
stored['address']
)
# Thresholds from UIDAI guidelines
if name_score >= submitted.get('match_threshold', 100):
return MatchResult(success=True)
return MatchResult(success=False, error_code="DEMO_MISMATCH")Deduplication and authentication
Q1/3 Demographic blocking uses gender (2) x age bucket (10) x state (36) x phonetic hash (100). If you dropped the state partition, roughly how many candidates would each enrollment be matched against?
Answer & explanation
Answer: B
Without state the reduction is 2 x 10 x 100 = 2,000x, so 1.4B / 2,000 is about 700,000 candidates, versus about 19,444 with all four partitions. 19,000 is the figure with state included; each partition you drop multiplies the matching work.
Q2/3 For one enrollment, ABIS 1 says UNIQUE, ABIS 2 says DUPLICATE and ABIS 3 says MANUAL_REVIEW. What does the orchestrator decide?
Answer & explanation
Answer: D
Automated decisions need at least 2 of 3 to agree; here every vendor says something different, so it goes to human adjudication. No single vendor's verdict decides on its own, which is the point of running three independent algorithms.
Q3/3 The auth service fires its audit log write with
asyncio.create_taskinstead of awaiting it. Why?Answer & explanation
Answer: B
Every authentication is still logged, but the response does not wait for the write, protecting the tight latency budget. Skipping or dropping logs is not the goal; residents can see their authentication history, so logging must happen.
Deep Dive 3: Security Architecture — Protecting Billion Biometrics#
Week 9 concepts: Security, encryption, zero-trust.
"Aadhaar stores the biometrics of 1.4 billion people. This is the ultimate honeypot. Security is existential."
Layer 1: Encryption everywhere
At capture:
- Biometrics encrypted on enrollment device
- 2048-bit PKI encryption
- Only CIDR has private key to decrypt
In transit:
- All communication over encrypted channels
- TLS 1.2+ mandatory
- Dedicated leased lines (not public internet)
At rest:
- AES-256 encryption for stored data
- Even within CIDR, data is encrypted
- Keys stored in HSM (Hardware Security Module)
Layer 2: Tamper detection
Every enrollment packet includes:
- HMAC for integrity verification
- Operator's biometric signature
- Supervisor's biometric signature (for exceptions)
- GPS coordinates of enrollment station
- Timestamp
- Device ID
Any tampering is detectable and traceable
Layer 3: Access control
CIDR access:
- Only UIDAI employees (very few)
- Multi-factor authentication required
- All access logged and audited
- No external access to raw biometrics
Partner access (AUA/ASA):
- Can only call authentication API
- Cannot query or download data
- Rate limited per entity
- Licensed and audited
Layer 4: Response minimization
Authentication returns ONLY:
- Yes (match) or No (no match)
- Transaction ID
- Timestamp
NEVER returns:
- Biometric data
- Demographic data (unless e-KYC with consent)
- Match scores
- Reason for failure (in detail)
This prevents information leakage
Privacy Features Added Over Time:
1. Virtual ID (VID) — Introduced 2018
- Problem: Every authentication exposes Aadhaar number
- Solution: 16-digit temporary ID that maps to Aadhaar
Resident generates VID → Uses VID instead of Aadhaar. Each VID is:
- Revocable (generate new anytime)
- Mappable only by CIDR
- Usable for authentication
AUA never sees actual Aadhaar number
2. Tokenization — For recurring services
- Problem: Same Aadhaar used at multiple services; services could collude to track user
- Solution: Each AUA gets a unique token for each Aadhaar
Aadhaar 1234-5678-9012 →
- Bank A: Token ABC123
- Telecom B: Token XYZ789
- Insurance C: Token PQR456
Services cannot correlate tokens
3. Masked Aadhaar
- Display:
XXXX-XXXX-9012 - Only last 4 digits visible
- Used for documents that need to show Aadhaar reference
4. Authentication history
Resident can see:
- Who authenticated their Aadhaar
- When
- What type of authentication
Provides transparency and detects misuse
# security/encryption_service.py
"""
Aadhaar Encryption Service
All biometric data is encrypted at the point of capture.
Only CIDR can decrypt using HSM-protected private keys.
"""
from dataclasses import dataclass
from typing import Tuple
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
import hmac
import hashlib
import os
class PIDBlockEncryption:
"""
PID (Personal Identity Data) Block encryption.
The PID block contains biometric/OTP data captured at
the authentication point. It's encrypted before transmission.
Encryption scheme (illustrative; UIDAI's API spec wraps the
session key with RSA/ECB/PKCS1Padding, this sketch uses OAEP):
1. Generate random session key (AES-256)
2. Encrypt biometric data with session key
3. Encrypt session key with UIDAI's public key (RSA-2048)
4. Add HMAC for integrity
"""
def __init__(self, uidai_public_key: bytes):
self.uidai_public_key = serialization.load_pem_public_key(
uidai_public_key
)
def encrypt_pid(
self,
biometric_data: bytes,
timestamp: str,
device_id: str
) -> Tuple[bytes, bytes]:
"""
Encrypt PID block for transmission to CIDR.
Returns:
(encrypted_data, encrypted_session_key)
"""
# Generate random session key
session_key = os.urandom(32) # 256 bits
iv = os.urandom(12) # 96 bits for GCM
# Create PID plaintext with metadata
pid_plaintext = self._create_pid_xml(
biometric_data=biometric_data,
timestamp=timestamp,
device_id=device_id
)
# Encrypt with AES-256-GCM
cipher = Cipher(algorithms.AES(session_key), modes.GCM(iv))
encryptor = cipher.encryptor()
ciphertext = encryptor.update(pid_plaintext) + encryptor.finalize()
# Encrypt session key with UIDAI's RSA public key
encrypted_session_key = self.uidai_public_key.encrypt(
session_key + iv, # Include IV
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
# Add HMAC for integrity
hmac_value = hmac.new(
session_key,
ciphertext,
hashlib.sha256
).digest()
return (ciphertext + encryptor.tag + hmac_value,
encrypted_session_key)
class HSMKeyManager:
"""
Hardware Security Module interface.
All cryptographic keys are stored and used within HSM.
Keys never leave the HSM in plaintext.
UIDAI requires FIPS 140-2 Level 3 (or higher) HSMs for partner
agencies' keys; assume the same class here.
"""
def __init__(self, hsm_connection):
self.hsm = hsm_connection
async def decrypt_pid(self, encrypted_pid: bytes,
encrypted_key: bytes) -> dict:
"""
Decrypt PID block inside HSM.
The private key never leaves the HSM.
Decryption happens entirely within the HSM.
"""
# Send to HSM for decryption
decrypted = await self.hsm.decrypt(
data=encrypted_pid,
encrypted_key=encrypted_key,
key_id="UIDAI_AUTH_PRIVATE_KEY",
algorithm="RSA-OAEP-256"
)
return self._parse_pid_xml(decrypted)
async def sign_response(self, response_data: bytes) -> bytes:
"""
Sign authentication response using HSM.
All responses are digitally signed so AUAs can
verify they came from authentic CIDR.
"""
signature = await self.hsm.sign(
data=response_data,
key_id="UIDAI_SIGNING_KEY",
algorithm="RSA-SHA256"
)
return signature
class AadhaarDataVault:
"""
Secure storage for Aadhaar numbers at AUA/KUA.
UIDAI mandates that any entity storing Aadhaar numbers
must use an "Aadhaar Data Vault" with:
- AES-256 encryption
- Keys in HSM
- Access logging
- No plaintext storage
"""
def __init__(self, hsm_client, database):
self.hsm = hsm_client
self.db = database
async def store_aadhaar(
self,
reference_id: str, # Your internal customer ID
aadhaar_number: str
):
"""
Store Aadhaar number securely.
"""
# Generate reference key in HSM
encrypted_aadhaar = await self.hsm.encrypt(
data=aadhaar_number.encode(),
key_id="AADHAAR_VAULT_KEY"
)
# Store only encrypted value
await self.db.insert({
'reference_id': reference_id,
'encrypted_aadhaar': encrypted_aadhaar,
'created_at': datetime.utcnow()
})
async def get_aadhaar(self, reference_id: str) -> str:
"""
Retrieve and decrypt Aadhaar number.
"""
record = await self.db.get(reference_id)
# Decrypt in HSM
aadhaar = await self.hsm.decrypt(
data=record['encrypted_aadhaar'],
key_id="AADHAAR_VAULT_KEY"
)
# Log access
await self._log_access(reference_id)
return aadhaar.decode()Deep Dive 4: Face Authentication — AI at Scale#
Week 10 concepts: Operational excellence, innovation.
"Face authentication was introduced in 2021 and has exploded to about 28 crore (280 million) transactions per month by late 2025, and 500+ crore in total by September 2026. It's AI-powered and developed in-house by UIDAI."
Why face authentication?
Problems with fingerprint:
- Manual laborers: Worn fingerprints
- Elderly: Faded fingerprints
- Amputees: Missing fingers
- Skin conditions: Temporary issues
- COVID-19: Hygiene concerns with touch-based
Face authentication advantages:
- Contactless (post-COVID preference)
- Works on any smartphone
- More inclusive (no physical requirements)
- Convenient for remote authentication
Growth trajectory
- Oct 2021 — Launch
- Jan 2025 — 100 crore (1 billion) cumulative
- Jul 2025 — 19.4 crore (194 million) in one month
- Aug 2025 — 200 crore (2 billion) cumulative
- Sep 2025 — 1.5 crore (15 million) per DAY (record)
- Nov 2025 — 28.3 crore (283 million) in one month
- Sep 2026 — 500+ crore (5 billion) cumulative
Adoption: nearly 200 government and private entities (September 2026)
# authentication/face_auth.py
"""
UIDAI Face Authentication Service
AI/ML-powered face matching developed in-house by UIDAI.
Uses liveness detection to prevent spoofing.
"""
from dataclasses import dataclass
from typing import Tuple
import numpy as np
@dataclass
class FaceAuthRequest:
aadhaar_or_vid: str
face_image: bytes # Captured selfie
liveness_data: dict # Blink detection, head movement
device_info: dict # Camera specs, OS version
@dataclass
class FaceAuthResult:
match: bool
confidence: float # 0-100
liveness_verified: bool
error_code: str = None
class FaceAuthenticationService:
"""
Face authentication with liveness detection.
Architecture:
1. Liveness detection (prevent photo/video attacks)
2. Face detection and alignment
3. Feature extraction (deep learning)
4. 1:1 matching against stored photo
"""
def __init__(
self,
face_detector,
liveness_model,
face_encoder,
match_threshold: float = 0.85
):
self.detector = face_detector
self.liveness = liveness_model
self.encoder = face_encoder
self.threshold = match_threshold
async def authenticate(
self,
request: FaceAuthRequest,
stored_photo: bytes
) -> FaceAuthResult:
"""
Perform face authentication.
Steps:
1. Verify liveness (not a photo/video)
2. Detect and align faces
3. Extract embeddings
4. Compare embeddings
"""
# Step 1: Liveness verification
liveness_result = await self._verify_liveness(
image=request.face_image,
liveness_data=request.liveness_data
)
if not liveness_result.is_live:
return FaceAuthResult(
match=False,
confidence=0,
liveness_verified=False,
error_code="LIVENESS_FAILED"
)
# Step 2: Face detection and quality check
captured_face = await self._detect_and_align(request.face_image)
stored_face = await self._detect_and_align(stored_photo)
if captured_face is None:
return FaceAuthResult(
match=False,
confidence=0,
liveness_verified=True,
error_code="FACE_NOT_DETECTED"
)
# Step 3: Extract face embeddings
captured_embedding = await self.encoder.encode(captured_face)
stored_embedding = await self.encoder.encode(stored_face)
# Step 4: Compare embeddings
similarity = self._cosine_similarity(
captured_embedding,
stored_embedding
)
# Convert to percentage
confidence = (similarity + 1) / 2 * 100
return FaceAuthResult(
match=confidence >= self.threshold * 100,
confidence=confidence,
liveness_verified=True
)
async def _verify_liveness(
self,
image: bytes,
liveness_data: dict
) -> LivenessResult:
"""
Verify the face is from a live person.
Liveness checks:
1. Texture analysis (detect printed photos)
2. Depth estimation (detect flat screens)
3. Eye blink detection (detect videos)
4. Random challenge-response (head movement)
"""
# Challenge: User was asked to blink/move head
challenge_type = liveness_data.get('challenge_type')
frames = liveness_data.get('frames', [])
if challenge_type == 'BLINK':
# Detect eye blink across frames
return await self.liveness.detect_blink(frames)
elif challenge_type == 'HEAD_TURN':
# Detect head movement
return await self.liveness.detect_head_movement(frames)
elif challenge_type == 'PASSIVE':
# Passive liveness (texture + depth analysis)
return await self.liveness.passive_check(image)
return LivenessResult(is_live=False)
def _cosine_similarity(
self,
embedding1: np.ndarray,
embedding2: np.ndarray
) -> float:
"""
Cosine similarity between face embeddings.
Range: -1 to 1 (higher = more similar)
"""
dot_product = np.dot(embedding1, embedding2)
norm1 = np.linalg.norm(embedding1)
norm2 = np.linalg.norm(embedding2)
return dot_product / (norm1 * norm2)
class LivenessDetector:
"""
Deep learning model for liveness detection.
Trained to distinguish:
- Real faces
- Printed photos
- Screen displays (replay attacks)
- 3D masks
"""
def __init__(self, model_path: str):
self.model = self._load_model(model_path)
async def passive_check(self, image: bytes) -> LivenessResult:
"""
Passive liveness without user action.
Analyzes:
- Moiré patterns (screen artifacts)
- Color distribution
- Texture frequency
- Specular reflection
"""
features = self._extract_liveness_features(image)
# Model predicts: real (1) vs spoof (0)
prediction = self.model.predict(features)
return LivenessResult(
is_live=prediction > 0.7,
confidence=prediction,
method="PASSIVE"
)
async def detect_blink(self, frames: list) -> LivenessResult:
"""
Detect eye blink across video frames.
A real person blinks; a photo doesn't.
"""
eye_aspect_ratios = []
for frame in frames:
# Detect eyes
eyes = self._detect_eyes(frame)
if eyes:
ear = self._eye_aspect_ratio(eyes)
eye_aspect_ratios.append(ear)
# Blink = dip in eye aspect ratio
if len(eye_aspect_ratios) > 5:
min_ear = min(eye_aspect_ratios)
max_ear = max(eye_aspect_ratios)
# Significant dip indicates blink
if (max_ear - min_ear) > 0.15:
return LivenessResult(is_live=True, method="BLINK")
return LivenessResult(is_live=False, method="BLINK")Security and face authentication
Q1/3 A resident uses the same Aadhaar at a bank, a telecom and an insurer. Why does each AUA get a different token for that Aadhaar?
Answer & explanation
Answer: A
Per-AUA tokens stop services from colluding: a bank token and a telecom token for the same person do not correlate. AUAs never receive biometrics at all, so tokens are not about decryption.
Q2/3 An encrypted PID block leaves a bank's fingerprint scanner. Where can it be decrypted?
Answer & explanation
Answer: C
The session key is wrapped with UIDAI's public key, and only the matching private key, held inside CIDR's HSM, can unwrap it. A public key can only encrypt; it is published widely precisely because it cannot decrypt.
Q3/3 Why does face authentication check liveness before comparing embeddings?
Answer & explanation
Answer: D
A photo of the real person matches the stored photo, so the system must first prove a live face is in front of the camera. The stored enrollment photo is trusted; liveness is about the capture, not cost ordering.
Phase 5: Scaling and Edge Cases#
"Aadhaar went from 0 to 1 billion enrollments in about 6 years. How did they scale the enrollment infrastructure?"
Enrollment at Village Scale#
The challenge
- 640,000+ villages in India
- Many with no electricity, no internet
- Extreme temperatures (deserts, mountains)
- Low literacy levels
- Need to enroll 1+ million people per day
The solution: mobile enrollment camps
Equipment transported to remote camps:
- Ruggedized laptops
- USB fingerprint scanners
- Iris cameras
- Web cameras (for photos)
- Portable generators
- Tables, chairs, canopies
Personnel:
- 150,000+ certified operators
- Supervisors for exception handling
- ~50 enrollments per station per day
Peak capacity (circa 2014):
- 60,000-80,000 enrollment stations
- 1+ million new enrollments per day
Edge Cases#
Edge case 1: Biometric exceptions
Problem: Not everyone can provide all biometrics
- Missing fingers (accidents, leprosy)
- Cataract patients (can't capture iris)
- Worn fingerprints (manual laborers)
- Children (biometrics change as they grow)
Solution:
- Multi-modal: If fingerprint fails, use iris
- Best-finger approach: Use whichever fingers work
- Exception handling: Supervisor approval for special cases
- Child enrollment: Mandatory biometric update at 5 and 15 years
- Biometric-exception enrollment (missing biometrics marked and photographed) for extreme cases
Edge case 2: Offline enrollment
Problem: No internet in remote villages
Solution:
- Enrollment client works entirely offline
- Packets stored locally (encrypted)
- Sync when connectivity available
- USB-based upload via registrar
- 30-day buffer for packet upload
Edge case 3: Duplicate enrollment attempts
Problem: People trying to get multiple Aadhaars for fraud
Solution:
- Three-way ABIS consensus
- Manual adjudication for borderline cases
- Reject if duplicate found
- Audit trail for investigation
- Criminal penalties for fraud
Edge case 4: Authentication failures
Problem: Genuine person fails authentication
Causes:
- Worn fingerprints (temporary or permanent)
- Cuts/injuries on fingers
- Wet/dirty fingers
- Sensor quality issues
- Aging (biometrics change over time)
Solution:
- Try multiple fingers
- Fall back to iris
- Fall back to OTP
- Face authentication option
- Biometric update facility
- Exception handling mode for genuine failures
Edge case 5: System under attack
Problem: DDoS or brute-force attacks
Mitigation:
- Rate limiting per AUA
- IP whitelisting for ASAs
- No direct internet exposure
- Anomaly detection
- Fallback to degraded mode
Enrollment scale and edge cases
Q1/3 At about 50 enrollments per station per day, how many stations must be running to sustain 1 million enrollments a day?
Answer & explanation
Answer: B
1,000,000 / 50 = 20,000 stations. The 60,000-80,000 peak stations give headroom for downtime, travel and slow days. 2,000 stations would manage only about 100,000 enrollments a day.
Q2/3 An elderly laborer's worn fingerprints fail authentication at a ration shop. What is the right system behavior?
Answer & explanation
Answer: C
The design assumes some genuine people cannot pass fingerprint checks, so it offers other fingers, iris, OTP and face rather than denying service. Lowering the global threshold would raise false accepts for everyone to fix a problem a fallback already solves.
Q3/3 An enrollment camp has had no connectivity for three weeks. What happens to its packets?
Answer & explanation
Answer: D
The client is offline-first: packets are stored encrypted, then synced or carried by USB when possible. Deduplication needs the full database, so it can only run at CIDR.
Phase 6: Monitoring and Operations#
"With 90 million daily authentications, operational excellence is critical."
Real-time metrics · Authentication rate
| Metric | Value |
|---|---|
| Current TPS | 1,042 auth/sec |
| Today's Total | 67.2M authentications |
| Success Rate | 99.2% |
| p99 Latency | 185ms |
By authentication type
| Type | Share |
|---|---|
| Fingerprint | 58% |
| OTP | 22% |
| Face | 12% |
| Iris | 5% |
| Demographic | 3% |
Top AUAs (by volume)
| AUA | Authentications |
|---|---|
| SBI Bank | 12.3M |
| NPCI/UPI | 9.8M |
| Jio Telecom | 7.2M |
| HDFC Bank | 5.6M |
| Others | 32.3M |
Infrastructure health
| Component | Status | Load |
|---|---|---|
| CIDR Bengaluru | Healthy | CPU: 45% |
| CIDR Manesar | Healthy | CPU: 42% |
| ABIS 1 | Healthy | Queue: 234 |
| ABIS 2 | Healthy | Queue: 189 |
| ABIS 3 | Healthy | Queue: 267 |
Operating the identity layer
Q1/3 An authentication response carries only yes/no, a transaction ID and a timestamp, never the match score. Why not return the score?
Answer & explanation
Answer: B
A numeric score tells an attacker whether each tweak brought them closer to a match (a hill-climbing attack). Returning only a decision minimises what any caller learns.
Q2/3 The dashboard shows queue depths for each ABIS vendor. What does a growing ABIS queue mean?
Answer & explanation
Answer: B
Deduplication runs asynchronously behind enrolment, so its backlog grows without touching the synchronous authentication path. That is exactly why it gets its own metric.
Q3/3 How does a Virtual ID reduce the risk of a resident being tracked across services?
Answer & explanation
Answer: B
With a VID, the agency never learns the actual number, and the resident can replace it at any time. Per-agency tokens add to this, so two services cannot join their records on a shared identifier.
Final Challenge: There Is No Password Reset for Fingerprints#
The interviewer sets down the marker. "Here's the scenario that keeps identity architects up at night."
"Investigators find fraud at a few hundred payment terminals: replayed biometric captures and fingerprints cloned in silicone, used to authenticate residents who were nowhere near. You can't reissue anyone's fingers. How does the system defend itself?"
- Some attacks resend captures that were recorded earlier
- Others present fake fingers made from copied prints
- The terminals belong to licensed agencies and look legitimate
- The residents affected never visited those terminals
Revoke what you can, strengthen what you can't
A fingerprint can't be rotated like a password. What can be revoked or tightened, and where would you detect each kind of attack?
One strong answer
- Stop replays at the sensor: only certified capture devices are accepted. Each one encrypts and signs the capture with its own device key, together with a timestamp and a unique transaction ID, so the server rejects stale or already-seen captures, and a compromised device's key can be revoked.
- Stop fakes with liveness: liveness detection on fingerprint and face capture rejects silicone and photos, and high-value operations require a second factor such as an OTP.
- Detect with the logs you already have: one device authenticating hundreds of different residents in an hour, or residents authenticating far from where they usually transact, is an anomaly to alert on and rate-limit per agency.
- Give residents controls: let them lock biometric authentication until they need it, and notify them of every authentication so misuse is spotted quickly.
- The principle: the biometric is a permanent identifier, not a secret. Security comes from what can be rotated (device keys, sessions, factors) wrapped around it.
Final challenge · Quest check
Q1/4 Yesterday's encrypted biometric capture is resent today. What should make the server reject it?
Answer & explanation
Answer: B
A replayed capture still contains a real fingerprint, so matching alone accepts it. Freshness and uniqueness, signed by a trusted device, are what turn a recording into a rejected request.
Q2/4 Why can't a fingerprint leak be handled the way a password leak is?
Answer & explanation
Answer: B
A password is a secret you can rotate. A biometric is a lifelong identifier, so the system protects the channel around it instead: trusted capture, liveness, extra factors and resident controls.
Q3/4 Which pattern most strongly suggests cloned fingerprints at a terminal?
Answer & explanation
Answer: B
Fraud with copied prints concentrates many identities on a few devices in odd places. Per-device and per-agency anomaly detection on the authentication log catches it, which is why authentication history is kept.
Q4/4 90 million authentications a day is about how many per second on average?
Answer & explanation
Answer: B
90,000,000 ÷ 86,400 ≈ 1,040 per second, and about 3,000 at a 3× peak: modest throughput, which is why the hard parts are the 200 ms latency and the security, not raw volume.
Interview Conclusion#
"Excellent walkthrough. A few quick questions:"
"What's the hardest part of building a system like Aadhaar?"
"Three things:
-
Biometric deduplication at scale — Proving uniqueness against 1.4 billion records requires clever biometric indexing, massive parallel matching, and multi-ABIS cross-checking. Brute force is mathematically impossible.
-
Inclusive design — The system must work for manual laborers with worn fingerprints, elderly with cataracts, amputees, and people in villages with no electricity. You can't just optimize for the happy path.
-
Security of the crown jewels — If the CIDR is breached, the biometrics of 1.4 billion people are exposed. There's no 'password reset' for fingerprints. The security architecture must be bulletproof."
"What lessons from Aadhaar apply to other large-scale systems?"
"Several key lessons:
-
API-first thinking — Aadhaar was designed as a platform from day one. The authentication API has enabled hundreds of services. DigiLocker, ABDM, e-KYC-based bank and UPI onboarding — all lean on Aadhaar's identity layer.
-
Vendor neutrality — Three ABIS vendors, open APIs, no lock-in. This enabled continuous improvement and prevented any single vendor from becoming critical.
-
Offline-first design — For systems that must work in challenging environments, assume no connectivity and design for sync.
-
Minimal data principle — Aadhaar authentication returns only Yes/No. This minimizes privacy exposure and attack surface.
-
Invest in inclusion — Adding face authentication made the system accessible to millions who struggled with fingerprints. Inclusion isn't just ethics — it's good engineering."
Summary: Concepts Applied from 10-Week Course#
Week 1: Data at scale
- Partitioning: Demographic blocking reduces search space
- Sharding: CIDR distributed across data centers
- Replication: Active-active for disaster recovery
Week 2: Failure-first design
- Offline-first: Enrollment works without connectivity
- Graceful degradation: Fall back to OTP if biometric fails
- Timeouts: 200ms latency budget for authentication
- Retry: Multi-finger, multi-modality attempts
Week 3: Messaging & async
- Queue-based: Enrollment packets queued for processing
- Async deduplication: Days/weeks for complex cases
- Audit trail: All transactions logged asynchronously
Week 4: Caching
- Template caching: Hot Aadhaars cached for fast auth
- Session caching: Reduce database lookups
- Pre-computed: Demographic indices for blocking
Week 5: Consistency
- Strong consistency: Deduplication must be accurate
- Consensus: 2-of-3 ABIS agreement for decisions
- Uniqueness guarantee: Core requirement of the system
Week 9: Security
- 2048-bit PKI encryption throughout
- HSM for key management
- Zero-trust architecture
- Virtual ID for privacy
- Tokenization to prevent tracking
Week 10: Operations
- Multi-vendor: Three ABIS for resilience
- SLOs:
<200msauth, 99.9% availability - Audit: Complete traceability of all operations
- Continuous innovation: Face auth, VID, e-KYC
Why Aadhaar Matters#
Scale
- Largest biometric database in history (1.4 billion)
- 170+ billion authentications (March 2026, and counting)
- 90+ million authentications per day
- Deduplication: Never done at billion scale before
Inclusion
- Works in 640,000 villages
- Handles worn fingerprints, cataracts, amputees
- Multiple modalities (finger, iris, face, OTP)
- Low-cost, portable enrollment kits
Impact
- ₹3.5+ lakh crore in DBT savings
- Millions of ghost beneficiaries eliminated
- Identity layer used by UPI onboarding, DigiLocker, ABDM, CoWIN
- "India Stack" model studied globally
Innovation
- One of the first multi-ABIS systems at national scale
- Virtual ID for privacy (before it was trendy)
- AI-powered face authentication at scale
- Consent-based data sharing (e-KYC)
"Aadhaar proved that identity infrastructure can be built as a public good, at billion scale, with inclusion at its core."
Self-Assessment Checklist#
After studying this case study, you should be able to:
Architecture:
- Design a biometric enrollment system with offline capability
- Explain deduplication challenges at billion scale
- Design authentication with sub-200ms latency
Distributed Systems:
- Apply demographic blocking to reduce search space
- Design multi-vendor consensus systems
- Handle offline-first data sync
Security:
- Implement end-to-end encryption with HSM
- Design privacy-preserving authentication (VID, tokenization)
- Apply zero-trust principles to sensitive data
Inclusion:
- Design for biometric edge cases
- Provide multiple authentication fallbacks
- Build systems for challenging infrastructure environments
Sources#
Official UIDAI Documentation:
- Read
UIDAI Official Website: https://uidai.gov.in/
- Paper
Aadhaar Authentication API Specification: https://uidai.gov.in/images/resource/Aadhaar_Authentication_API-2.5_Revision-1_of_January_2022.pdf
- Paper
Aadhaar e-KYC API Specification: https://uidai.gov.in/images/resource/aadhaar_ekyc_api_2_5.pdf
- Read
UIDAI Operation Model: https://uidai.gov.in/en/ecosystem/authentication-ecosystem/operation-model.html
- Read
Network and Server Security: https://uidai.gov.in/en/16-english-uk/aapka-aadhaar/31-network-and-server-security.html
- Read
Enrollment Data Security: https://uidai.gov.in/en/16-english-uk/aapka-aadhaar/33-enrolment-data-security.html
- Paper
About Aadhaar Paperless Offline e-KYC: https://uidai.gov.in/en/ecosystem/authentication-devices-documents/about-aadhaar-paperless-offline-e-kyc.html
Statistics and Press Releases:
- Read
UIDAI January 2025 Statistics: https://www.pib.gov.in/PressReleasePage.aspx?PRID=2100685
- Read
Aadhaar Authentication Surpasses 150 Billion: https://www.pib.gov.in/PressReleasePage.aspx?PRID=2129121
- Read
UIDAI August 2025 Statistics: https://www.pib.gov.in/PressReleasePage.aspx?PRID=2163733
- Paper
UIDAI November 2025 Statistics: https://uidai.gov.in/images/UIDAI_records_231_crore_Aadhaar_authentication_transactions_in_November_2025.pdf
- Blog
Asianet Newsable - 134 Crore Live Holders, 17,000+ Crore Authentications (March 2026): https://newsable.asianetnews.com/amp/business/indias-aadhaar-system-serves-134-crore-users-logs-17000-cr-txns-articleshow-puyyepc
- Read
UIDAI Enrolment and Authentication Dashboards: https://uidai.gov.in/en/aadhaar-dashboard/enrollment-dashboard
- Paper
UIDAI Aadhaar Saturation Report (June 2026): https://mndc.uidai.gov.in/images/AadhaarSaturationReport.pdf
- Read
UIDAI Chairman at Global Fintech Fest 2026 (AIR): https://newsonair.gov.in/uidai-launches-new-software-to-simplify-aadhaar-authentication/
- Read
Face Authentication Crosses 500 Crore (September 2026): https://www.sarkaritel.com/?p=418161
- Read
DBT Savings Estimate (April 2025): https://www.businesstoday.in/amp/latest/economy/story/dbt-system-delivers-big-rs-348-lakh-crore-saved-subsidy-share-falls-to-9-473240-2025-04-23
- Read
IBEF Aadhaar Coverage: https://ibef.org/news/aadhaar-authentication-crosses-150-billion-transactions-powering-india-s-digital-economy-and-welfare-services
- Read
MobileIDWorld Coverage: https://mobileidworld.com/indias-aadhaar-digital-id-system-surpasses-150-billion-authentication-transactions/
- Read
OpenGov Asia Analysis: https://opengovasia.com/2025/02/10/aadhaars-expanding-role-in-indias-digital-economy/
Architecture and Technical Deep Dives:
- Blog
HPE Developer Portal - Aadhaar Experience: https://developer.hpe.com/blog/architecting-the-worlds-largest-biometric-identity-system-the-aadhaar-ex/
- Blog
MapR - Aadhaar Architecture: https://mapr.com/blog/architecting-worlds-largest-biometric-identity-system-aadhaar-experience/
- Read
Teknonauts - Aadhaar Architecture: https://teknonauts.com/aadhaar-card-architecture/
- Read
Medium - Aadhaar System Design: https://medium.com/career-drill/aadhar-system-design-39b1425a0983
- Paper
IACR ePrint - Aadhaar Security Analysis: https://eprint.iacr.org/2022/481.pdf
Biometric Technology:
- Read
Neurotechnology and TCS ABIS: https://www.biometricupdate.com/202103/neurotechnology-to-provide-biometric-de-duplication-software-for-indias-aadhaar-program
- Read
Neurotechnology Press Release: https://www.neurotechnology.com/press_release_india_uidai_aadhaar_id.html
- Read
MegaMatcher ABIS: https://www.neurotechnology.com/megamatcher-abis.html
- Paper
UIDAI Role of Biometric Technology: https://www.dematerialisedid.com/PDFs/role_of_biometric_technology_in_aadhaar_jan21_2012.pdf
Security and Privacy:
- Read
Privacy International Analysis: https://privacyinternational.org/case-study/4698/id-systems-analysed-aadhaar
- Blog
Protean Tech Security Overview: https://www.proteantech.in/articles/aadhaar-authentication-data-security-27-06-2025/
- Blog
AWS Aadhaar Data Vault: https://aws.amazon.com/blogs/publicsector/build-aadhaar-data-vault-aws/
Academic and Research:
- Read
Taylor & Francis - Aadhaar Governing with Biometrics: https://www.tandfonline.com/doi/full/10.1080/00856401.2019.1595343
- Blog
ScienceDirect - Decoding Indian Data Governance: https://www.sciencedirect.com/science/article/pii/S2590291125001354
Further Reading#
Official Documentation:
- Docs
UIDAI Developer Portal: https://uidai.gov.in/en/ecosystem/authentication-devices-documents.html
- Read
Authentication Regulations: https://uidai.gov.in/legal-framework/aadhaar-regulations.html
- Read
Aadhaar Act 2016: Full text with amendments
Engineering Talks:
- Read
Dr. Pramod Varma - "Architecting World's Largest Biometric Identity System" (Strata+Hadoop World 2014)
- Talk
Nandan Nilekani - Various talks on India Stack and Aadhaar design philosophy
Engineering Blogs:
- Read
ByteByteGo: System design breakdowns
- Read
High Scalability: Case studies on large-scale systems
- Read
Biometric Update: Aadhaar and biometric technology coverage
Books:
- Book
"Designing Data-Intensive Applications" by Martin Kleppmann — Distributed systems fundamentals
- Book
"Aadhaar: A Biometric History of India's 12-Digit Revolution" by Shankkar Aiyar
- Book
"The Aadhaar Effect" by N.S. Ramnath and Charles Assisi
Related Systems to Study:
- Read
Estonia e-Residency: European digital identity model
- Read
Singapore SingPass: National digital identity
- Read
UK Verify: Federated identity approach (contrast to centralized; retired in 2023)
- Book
MOSIP: Open-source identity platform (inspired by Aadhaar)
Research Papers:
- Read
"India's Aadhaar: Structure, Security, and Vulnerabilities" — IACR ePrint 2022/481
- Paper
IEEE/ACM papers on biometric deduplication at scale
"1.4 billion unique identities. 170 billion authentications and counting. The foundation of India's digital public infrastructure."
this lesson in shorts · 4
play them all →One idea from this lesson in about a minute each, made for your phone.
- 0/7stages cleared
Follow-up drills
0 of 10 practisedYou presented Aadhaar (UIDAI). Now the interviewer pushes back. Answer each follow-up out loud or in the box, then reveal a strong answer.
“Why can't you just compare every new enrolment against everyone already enrolled? Show me the numbers.”
Pushes on · whether brute-force deduplication is even possible
Go deeper · reading trail
all reading →Real write-ups from the teams that built these systems. Each opens on the publisher's site in a new tab.
Aadhaar Technology & Architecture: Principles, Design, Best Practices, & Key Lessons
2014- What
- UIDAI's own 2014 description of the Aadhaar system, kept on the Internet Archive.
- Key idea
- Openness, linear scalability, security and vendor neutrality; three ABIS vendors deduplicate every enrolment, on open-source stacks with Hadoop, HBase and MongoDB.
- Why here
- The primary source for this quest's multi-ABIS deduplication and enrolment pipeline.
UIDAI no longer hosts this PDF; the Internet Archive copy was opened and its text checked on 2026-10-18.
UIDAI Specifications
- What
- UIDAI's specification for apps and portals that authenticate residents through the Aadhaar app.
- Key idea
- An authenticating agency's app or portal calls the Aadhaar app by app-to-app intent or QR code for face authentication and credential exchange, on Android and iOS.
- Why here
- The current face-authentication path from this quest's deep dive, as UIDAI documents it today.
New lessons by email
New lessons, side quests and case files as they ship, at most once a week. No spam, unsubscribe anytime.
Discussion · …
Public: everyone reading this lesson sees comments. Be kind and keep it on the material.



