When someone hands their phone to a friend to show a photo, the last thing they want is that friend casually opening their personal finance app and browsing their account balances or net worth.
Mobile authentication is the front door of personal privacy.
Yet many Android apps implement authentication with alarming vulnerabilities:
- Storing a plaintext 4-digit PIN in unencrypted
SharedPreferences. - Using weak, unsalted MD5 or SHA-256 hashes vulnerable to instant precomputed rainbow table attacks (a 4-digit numeric PIN has only 10,000 possible combinations, which a modern smartphone CPU can crack in less than 2 milliseconds).
- Using vulnerable string comparison methods (
pin == inputPin) susceptible to CPU execution side-channel timing attacks.
Here is how FinLedger architected its authentication subsystem during Hardening Sprint H8, combining hardware-backed Android BiometricPrompt with 50,000-iteration salted PBKDF2 PIN hashing and constant-time verification.
The Problem With 4-Digit PINs
A 4-digit numeric PIN has a fundamental mathematical limitation: the keyspace is tiny.
From 0000 to 9999, there are exactly 10,000 possible passwords. If an attacker extracts a standard SHA-256 hash of a 4-digit PIN:
$$\text{SHA-256}("1234") = \text{03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4}$$
A single CPU core can compute all 10,000 SHA-256 hashes in approximately 1.8 milliseconds. Without proper cryptographic key stretching and salting, a 4-digit hash provides virtually zero protection against an extracted database.
flowchart TD
subgraph WeakHashing["The Fast Hash Vulnerability"]
direction TB
WeakPIN["4-Digit PIN (Only 10,000 Possible Combinations)"] --> FastAlgo["Standard SHA-256 / MD5 Hash"]
FastAlgo --> FastCrack["Cracked in under 2 milliseconds via Brute-Force!"]
end
subgraph HardenedPBKDF2["FinLedger PBKDF2 Key Stretching"]
direction TB
PINInput["User 4-Digit PIN"] --> Salt["16-Byte Cryptographically Secure Salt (SecureRandom)"]
Salt --> PBKDF2["PBKDF2 with HMAC-SHA256:
50,000 Sequential Iterations"]
PBKDF2 --> HardenedHash["Computationally Expensive Derived Hash:
Defeats GPU / ASIC Brute-Force Exhaustion"]
end
The Solution: Salted PBKDF2 with 50,000 Iterations
To secure numeric PINs, FinLedger implements Password-Based Key Derivation Function 2 (PBKDF2) combined with HMAC-SHA256:
-
Unique 16-Byte Random Salt: When the user configures their PIN, the
SecurityManagergenerates a cryptographically random 16-byte salt usingjava.security.SecureRandom. This ensures that even if two users choose the identical PIN (1234), their stored hashes are completely different, defeating precomputed rainbow tables. - 50,000 Iteration Stretching: PBKDF2 applies a pseudo-random function sequentially 50,000 times. This intentionally introduces a 30-millisecond computational delay for a single verification. For a legitimate user entering their PIN once, 30ms is completely imperceptible. But for an attacker attempting to brute-force all 10,000 combinations, the total attack time increases from 1.8 milliseconds to over 5 minutes of intensive computation.
Eliminating Timing Attacks with Constant-Time Comparison
In standard Java/Kotlin, comparing two strings using stringA == stringB or stringA.equals(stringB) uses an early-exit comparison: it checks characters one by one and returns false the microsecond it finds a mismatch.
In high-security environments, an attacker measuring CPU instruction cycles can deduce which character was incorrect based on how many nanoseconds the comparison took (a timing side-channel attack).
FinLedger verifies PIN hashes using constant-time verification:
MessageDigest.isEqual(storedHashBytes, computedHashBytes)
MessageDigest.isEqual always compares every single byte in the array regardless of where a mismatch occurs, ensuring that the execution time is strictly identical whether the first byte or the last byte differed.
Progressive Rate Limiting and Lockouts
To stop automated brute-force attacks on the physical device itself, FinLedger enforces a progressive lockout state machine:
stateDiagram-v2
[*] --> Unlocked
Unlocked --> Locked : App Backgrounded / Timeout
Locked --> Unlocked : Valid Biometric or PIN
Locked --> AttemptFailed : Incorrect PIN
AttemptFailed --> Locked : Failed Attempts < 5
AttemptFailed --> SoftLockout : 5 Failed Attempts (30s Lockout)
SoftLockout --> HardLockout : 10 Failed Attempts (60s Lockout)
HardLockout --> MaximumLockout : 15 Failed Attempts (300s Lockout)
- 5 Failed Attempts: Temporary 30-second lockout.
- 10 Failed Attempts: Temporary 60-second lockout.
- 15 Failed Attempts: Full 5-minute (300-second) lockout.
- Lockout counters and expiration timestamps are stored inside Android Keystore-backed
EncryptedSharedPreferences, ensuring that restarting the application cannot bypass active lockout countdowns.
The Zero-Lockout Migration Strategy
In early development builds, FinLedger utilized a simpler single-round SHA-256 hash. When we upgraded to 50,000-iteration PBKDF2 in Sprint H8, we faced a critical deployment challenge: how do you upgrade existing user PINs without forcing everyone to reset their accounts?
We engineered a Zero-Lockout Auto-Upgrade Migration:
- When a user with an older hash logs in, the
SecurityManagerverifies the entered PIN against the legacy SHA-256 algorithm. - The instant authentication succeeds, the engine takes the verified plaintext PIN in memory, generates a fresh 16-byte random salt, stretches it through 50,000 iterations of PBKDF2, saves the new hardened hash, and permanently purges the legacy hash.
- The user experiences zero friction, and their security is transparently upgraded to modern standards on their very next login.
Android BiometricPrompt Integration
Alongside the hardened PIN, FinLedger integrates directly with the AndroidX BiometricPrompt API:
- Allows instant authentication using fingerprint sensors or secure 3D face recognition.
- Uses
BIOMETRIC_STRONGcrypto-based authentication backed by the hardware TEE (Trusted Execution Environment). - If biometric authentication fails or the sensor is dirty, the user seamlessly falls back to the PBKDF2 PIN keypad.
Key Takeaway
A 4-digit PIN can be simple for the user without being easy for an attacker.
By combining hardware-backed biometrics with 50,000 iterations of salted PBKDF2, constant-time verification, and progressive lockout state machines, FinLedger delivers consumer convenience backed by defense-grade security.
FinLedger is a privacy-first, offline-first personal finance application for Android that models bank accounts, credit cards, and cash without double-counting.
Explore FinLedger on Google Play: https://play.google.com/store/apps/details?id=com.devanshufinance.app

Top comments (0)