TT Lab
Get started
Learn Learning paths Courses

The Language of Banking

Proving Who Raised the Limit

Continue in TT Lab

Goal

You bring out in numbers the evidence that vanished through in-place UPDATEs, build an append-only audit record with canonical serialization and a hash chain, find tampering in a vendor export copy, and attach HMAC and a correlation ID to produce an evidence bundle.

Why it matters

What is disputed in an audit is not the value but the history. A system that stores only the current limit cannot answer "who raised it, when, and why," and without that answer, even a correct value becomes a value you cannot explain. A hash chain is not a device that makes records impossible to fix but one that makes them show when fixed. That is why you first need canonical serialization so that the same event is always the same bytes, and you need a key to stop forgery that recomputes the whole chain. The grader does not trust the hashes you wrote. It recomputes from the original from the beginning and compares, and tests your tools with random vectors that differ on each run and a chain it builds freshly.

Steps

  1. Save the material script as /root/audit/gen_audit.py and run it to make /root/audit/core.db and /root/audit/vendor_chain.jsonl.
  2. Reconcile the month-opening snapshot against the current limits and write the vanished evidence into /root/audit/lost.txt as changed, ticketed, untraceable, mismatched, and raised_total.
  3. Create /root/audit/canon.py so that, for each JSON line on standard input, it outputs one line with the sha256 of the canonically serialized bytes.
  4. With /root/audit/build_chain.py, move the 400 lines of app_log into the audit_log table of /root/audit/audit.db as a hash chain.
  5. Verify the vendor export copy with /root/audit/verify_chain.py and write the places found into /root/audit/tamper.txt.
  6. Put a MAC on top of the chain with /root/audit/hmac.key and /root/audit/mac_chain.py to make /root/audit/audit_mac.jsonl.
  7. Regroup events per request with the correlation ID and write them into /root/audit/corr.txt.
  8. Produce the evidence bundle with /root/audit/evidence/manifest.json and /root/audit/audit_report.md.

Notes

Build the limit snapshot and the vendor export copy

Save the material script as /root/audit/gen_audit.py and run it to make /root/audit/core.db and /root/audit/vendor_chain.jsonl.

First create /root/audit and run with python3 in it. There are four tables, limit_opening, customer_limit, change_ticket, and app_log, and the vendor chain is 399 lines.

Count what the in-place UPDATE erased

Reconcile the month-opening snapshot against the current limits and write changed, ticketed, untraceable, mismatched, and raised_total into /root/audit/lost.txt.

Join limit_opening and customer_limit by customer_id and count the customers whose limit differs. Among them, the ones with no change_ticket are changes nobody can explain, and the ones where the application's new_limit differs from the current value are where approval and reflection split.

Make the same event always the same bytes

Create /root/audit/canon.py so that, for each JSON line on standard input, it outputs one line with the sha256 of the canonically serialized bytes.

Three things are all there is: key sorting, delimiters without spaces, and UTF-8 that does not escape non-ASCII. Look at sort_keys, separators, and ensure_ascii of Python's json.dumps. The keys of nested objects must be sorted too.

Bind 400 log lines into a hash chain

With /root/audit/build_chain.py, move app_log into the audit_log table of /root/audit/audit.db and fill in prev_hash and entry_hash.

The columns are seq, ts, corr_id, actor, action, target, amount, prev_hash, and entry_hash. entry_hash is the sha256 of the canonically serialized bytes of the object bundling the first seven values and prev_hash, and the prev_hash of the first entry is sixty-four zeros. The grader recomputes from core.db and compares.

Find the tampered places in the vendor export copy

Verify vendor_chain.jsonl with /root/audit/verify_chain.py and write content_bad_seq, link_bad_seq, missing_seq, and total_lines into /root/audit/tamper.txt.

The vendor chain's hash is the sha256 of the string joining prev_hash, seq, ts, actor, action, target, and amount with vertical bars. At a place where the content was fixed, that line's entry_hash goes off, and at a place where an entry is missing, the next line's prev_hash goes off. The two symptoms differ.

Put a key on top of the chain

With /root/audit/hmac.key (64 hexadecimal characters, permission 600) and /root/audit/mac_chain.py, make /root/audit/audit_mac.jsonl.

Make the key with openssl rand -hex 32 and chmod 600. The MAC is HMAC-SHA256 over the canonically serialized bytes of the three keys entry_hash, prev_mac, and seq, and the prev_mac of the first line is sixty-four zeros. The key string must not go into the record file.

Regroup one event into one request

Regroup events by correlation ID and write no_corr, corr_groups, corr_of_seq140, target_of_seq140, events_in_group, and actors_in_group into /root/audit/corr.txt.

For a line whose corr_id is an empty string, you cannot later restore which request it was part of. seq 140 is exactly the place where the content was off in the vendor export copy. Rescan app_log with that event's corr_id and see how many systems it passed through.

Produce the evidence bundle and the report

Put files, chain, verified, and vendor_findings in /root/audit/evidence/manifest.json, and write a five-section report in /root/audit/audit_report.md.

The files in the manifest is an array of objects with path, sha256, and bytes, and audit.db, audit_mac.jsonl, and vendor_chain.jsonl must all be in it. In chain, write rows, genesis, and head. The grader recomputes the hashes you wrote from the real files and compares. The section titles of the report are: What was confirmed, Where the evidence was lost, The chain and verification, The vendor export copy, and Recommendations.