Event, attempt and acknowledgement
Start with the contract: what counts as an action, how is an attempt identified and when may the producer delete its copy? An emitter can reuse sequence numbers after restarting. In this model, identity is (source, epoch, id); receivedAt describes a receipt and does not create another action. Retain repeated deliveries for diagnosis and construct a separate unique-action view. Using only source and sequence can logically remove a legitimate event after a new boot. In an example protocol, the producer deletes its copy after a persistence ACK. If ACK is sent while bytes are still only in memory, failure in that window can lose the sole copy. Testing this condition requires a persistence definition appropriate to the system and a controlled failure. This lesson’s lab writes JSONL and calls fsync but does not cut power or establish disk, controller or filesystem guarantees under such failure. Use it to observe the flow, not to conclude production durability.
Fix the bytes and minimize before authenticating
The producer serializes only the event as JSON with sorted keys, compact separators, ensure_ascii=False and UTF-8. The exact sequence is stored in payloadB64; HMAC-SHA256 is computed over decoded bytes, not the JSONL envelope. The verifier initially reads still-untrusted fields to locate the key and produce diagnostics. It compares bytes with compare_digest; accepting the interpretation as authenticated depends on that comparison and policy. Changing 7 to 7.0 or rearranging bytes can change the outcome even when numeric analysis looks equivalent. A legitimate transformation must be documented rather than presented as the same authenticated object. HMAC does not encrypt. If an event retains an account number as readable text, anyone reading the payload can still see it. For a service needing only process identifier P, remove the number at the producer and authenticate the minimized representation. Removing it at the collector while retaining the original MAC changes the bytes protected by the reference. Collector-added receivedAt is outside the payload: claims about that time require their own evidence.
Four results without shortcuts to success
Use four columns in the workpaper. Availability: could the indicated key generation be loaded? Mathematics: does the MAC calculated over the bytes match the received value? Policy: is use of that generation and period permitted? Completeness: is the expected inventory represented without unexpected identities or improper duplicates? Missing K1 produces macMatches=null rather than false. A present but wrong key produces false. A compromised key can produce true in the comparison while being rejected by policy. In batch A, six identities were emitted. The omitted variant retains five valid MACs but lacks A103; duplicated retains seven valid receipts for six identities. Neither equals a normal exactly-once batch. The producer inventory is a separate file written before collection and serves as the teaching emission reference. It is controlled by the same local user and does not prove independent custody. In a real system, establish who can change inventory, policy and events before attributing strength to reconciliation.
Rotate without confusing issuance and history
Associate each generation with an immutable identifier, protected material and usage windows. Lab policy permits K1 event creation before rotationTick and K2 from that point; both generations remain available for historical processing through tick 200. Verification occurs at fictional tick 100. These are neither NIST-recommended durations nor KMS rules. They make the distinction between applying new protection and processing previously protected information visible. Before switching a producer, confirm that every required consumer can verify the new generation. Example: C2 supports K2 at 10:10, K1 issuance ends at 10:15 and trial plus switch take no more than two minutes. Testing compatibility and switching within that window retains continuity; removing K1 from verifiers would remove still-required historical capability. Recycling name K1 with new bytes makes history ambiguous. Authorization for old use must not re-enable new issuance merely to resolve a historical-read problem.
Compromise does not change the mathematics
In compromised, the same key remains available and every MAC may match, but the trust table marks the key compromised. The program preserves both facts. Changing compromised to false makes the verifier accept the policy again; this proves configuration influences the decision, not that the key is no longer exposed. Do not use time claimed inside a payload as external proof of pre-compromise creation: a key holder can choose that field. In a different case, an independently controlled archive has retained bytes and MAC since 10:30, while earliest possible exposure is 11:00. That reference can meet an explicit historical-trust policy. A second record received at 12:00 and dated 10:00 without an earlier anchor has different support. Preserve it and report uncertainty; lack of corroboration does not demonstrate forgery. Analysis involving a compromised secret requires authorization and an explanation of consequences.
Secret holders and attribution
Any entity knowing the HMAC key and format can generate a MAC the verifier accepts mathematically. The holder-forged variant adds an EXTRA event using the same secret, outside the producer inventory. Comparison passes; reconciliation detects the unexpected identity. This variant detects the EXTRA identity. The verifier inventory compares identities only: a holder can also replace amount for an expected identity and recompute its MAC. In that case allLayersPass can remain true even without changing the inventory. This is neither individual attribution nor full value reconciliation. Changing the reference could additionally conceal unexpected identities. A producerId field inside the payload does not turn a shared secret into an individual signature. The exercise uses real subprocesses, but all belong to the same OS user. Key files are disposable with restrictive local permissions; they are not HSMs, KMSs or independent administrative boundaries. To assess a real design, seek evidence of generation, distribution, use permissions, export, trust-store management and recovery. A non-exportable key can still be misused through an over-permitted Decrypt API. Assess the permitted operation and protected object as well as secret storage.
Execute and predict the eight variants
Download the lesson materials, extract them into a working folder and open a terminal there. No Docker, network, credentials or additional libraries are required. Run python3 lab.py run --fixture fixture-A.json --out run-A and then python3 check_expected.py run-A. Repeat with fixture-B.json and run-B. Each destination must be new. The two inventories contain six and seven events; disposable random keys make output bytes differ between executions. Before opening result.json, predict: baseline passes; omitted lacks one identity; duplicated has two target receipts; modified has one MAC mismatch; rotation uses two generations; old-key-missing has three comparisons not performed; compromised has valid comparisons with policy refusal; holder-forged has an unexpected identity despite a valid MAC. The separate oracle uses literal A/B ID and amount lists plus outcome matrices without importing the verifier. This provides computational independence, not independent human review. emitted.jsonl, collected.jsonl, producer-inventory.json and policy.json let you reproduce the comparison.
Change an assumption and write the assessment
Use a copy of the compromised policy. Retain events and change only the key’s state to uncompromised; the README supplies a verify command creating a new result file without replacing the previous one. Predict which columns change. Restore compromised status and move the creation window to exclude the last event; compare policyDecision with macMatches. You can also restore K1.withheld-for-exercise to the keyring in a copy of old-key-missing and repeat. This restores comparison capability without certifying key custody. Guided case: six events are received, one duplicated during transport; three need K1 and the table marks compromise. Write four separate conclusions and a limitation concerning the inventory source. Identify additional controls for historical trust and the authority approving use. Summary: do not replace unknown results with failure or success; do not remove originals to obtain a green report; do not attribute receipt timestamps to MAC coverage. Connect the exercise to PKI, evidence retention, revocation, monitoring and custody controls.
Exercise files
Python without external dependencies, two inventories, eight variants and PT/EN worksheet.
A fund close produces events before and after rotation. One event is duplicated, another is lost and a key becomes unavailable. The auditor must distinguish delivery, integrity and trust.
Common pitfalls
Confusing a valid MAC with a complete batch; reusing key identifiers; treating local processes as security boundaries; turning key unavailability into mathematical mismatch.
Related topics: Key management · Log management · Incident evidence · Public key infrastructure
Record presence, key availability, mathematical comparison and trust decision separately. Explain which source supports each outcome and which conclusion remains open.
References
- Guide to Computer Security Log Management · SP 800-92, final September 2006
- hmac — Keyed-Hashing for Message Authentication · Documentation3.14.8 observed; lab interpreter3.13.1
- Security and Privacy Controls for Information Systems and Organizations · SP800-53 Rev.5 December2020 PDF; release5.2.0 noted2025-08-27
- Recommendation for Key Management: Part 1 – General · SP 800-57 Part 1 Rev.5, final May 2020