Milkiyat
A tamper-evident land registry built on a signed hash chain.
Milkiyat is a desktop land-records system where every approved registration or transfer becomes a block on a SHA-256 hash chain, signed by the approving surveyor and encrypted at rest. Any edit, even one made directly in the database, is detected.
The problem
Land ownership records are a high-value target. In many systems a single person with database access can change an owner's name, and nothing records that it happened.
The goal was a registry where records can't be silently altered, every approval can be traced to a person who can't deny it, and personal data stays confidential, all on a single offline workstation, as a records office would use it.
What I built
- Hash-chained ledger where each block's SHA-256 hash covers its plaintext data and the previous block's hash.
- Field-level encryption of owner names and locations with AES-256-GCM and a fresh nonce per field.
- Digital signatures: every approved block is signed with the surveyor's own RSA-2048 key (PSS, SHA-256).
- Four roles (Citizen, Registrar, Surveyor, Admin) with separation of duties, two-factor login and a full audit log.
- One-click integrity verification that reports exactly which block failed and why.
Architecture
Key decisions
Hash the plaintext, not the ciphertext
Even an attacker holding the key who re-encrypts a forged owner name produces valid ciphertext, but the forged plaintext no longer matches the stored hash.
Per-user signing keys, encrypted with the user's password
Private keys are only decrypted in memory for a session, so approvals are bound to a specific surveyor and can't be made in their name.
Admins can't create or sign records
Separation of duties: the account that manages users and audits the chain has no code path to write to it.
Canonical JSON with sorted keys
The same data always hashes to the same value, and numeric fields are normalised so SQLite round-trips don't cause false alarms.
Security
- PBKDF2-HMAC-SHA256 password hashing, 200,000 iterations, random salt per user
- Constant-time comparisons with
hmac.compare_digest - RFC 6238 TOTP implemented from scratch, compatible with Google Authenticator
- Audit trail of logins, failed logins, requests, approvals and verification runs
- Tested end to end, including a direct-database tampering attempt that verification caught
Results
What I'd improve next
- Move from one node to a permissioned multi-node ledger (e.g. Hyperledger Fabric) with consensus
- Store the master key in an HSM or managed KMS with rotation
- Make two-factor mandatory for signing roles
- Independent security audit before handling real ownership data