Recover phone numbers from hashed MPesa Daraja callbacks. <1ms lookups, pure Python stdlib.
# MSISDN Binary Search Hash Lookup
Reverse-lookup SHA256 hashes of Kenyan phone numbers from MPesa Daraja API callbacks. Sub-millisecond lookups against a 2.8 GB sorted binary file. Pure Python stdlib for the core; `boto3` only if you want S3 upload or Lambda.
---
## The problem
Safaricom's MPesa Daraja API used to include the subscriber's phone number in plain text (partially masked) in payment callbacks — for example, in C2B Pay Bill callbacks:
```json
{
"TransactionType": "Pay Bill",
"TransID": "RKL51ZDR4F",
"TransTime": "20231121121325",
"TransAmount": "5.00",
"BusinessShortCode": "600966",
"BillRefNumber": "Sample Transaction",
"InvoiceNumber": "",
"OrgAccountBalance": "25.00",
"ThirdPartyTransID": "",
"MSISDN": "fc418dcfe94c732a...", // ← SHA256 hash of the phone number (used to be plaintext e.g. "2547 ***** 126")
"FirstName": "NICHOLAS",
"MiddleName": "",
"LastName": ""
}
```
SHA256 is a one-way function — you cannot mathematically reverse it. The only way to recover the original phone number is to hash every possible candidate and check for a match.
For Kenyan Safaricom numbers this means checking up to 200 million candidates (`2547XXXXXXXX` and `2541XXXXXXXX`). Doing that on every callback request is not viable.
---
## Why not other approaches
| Approach | Lookup time | Notes |
|---|---|---|
| Brute-force per request | ~2 min | Hashes 200M numbers on every lookup. Completely unusable in practice. |
| Store hashes in a database (SQLite, Postgres) | ~5–50 ms | Requires a DB server or large SQLite file (~15 GB with indexes), plus query overhead. |
| Rainbow tables | Saves some space | Complex to implement correctly, slower than direct lookup, and the search space here is small enough not to need them. |
| **Sorted binary file + binary search** | ** **Note on file size.** Each record stores the first 10 bytes (80 bits) of the SHA-256 plus a 4-byte index, for 14 bytes per record. Birthday-collision probability across all 200M MSISDNs is ~1.6 × …