5. Tamper Detection & Validation
Validation sequence
Run checks in this order on every card read. Stop and mark tampered on the first failure.
- Uninitialised card pre-check — if the
magicfield is all0x00or all0xFF, treat the card as uninitialised (not tampered) and halt. Display "Unactivated card" to the user. Do not proceed to step 1. See System Design §18. - Magic and version check — reject the payload if
magicdoes not match the expected value orversionis unsupported. - Key version lookup — reject if no session grant is available for the card's
keyVersion. - HMAC verification — recompute HMAC over encrypted buffer and trailer fields; reject on mismatch.
- AES-GCM decryption — attempt decryption; reject if the GCM authentication tag fails.
- Counter check — reject if the on-card
counteris less than or equal to the last known counter for this card (rollback). - Timestamp check — reject if
lastTimestampis significantly in the future (beyond clock drift allowance). - Status check — if
statusis any blocked value, deny writes; allow read-only operations. - Balance consistency — reject if
balance ≠ lastBalancewhen no transaction was expected, or ifbalancedoes not match thebalanceAfterof the most recent log entry. - Log chain verification — recompute each entry hash from
session.startTime; reject if any hash mismatches. - Root hash verification — recompute
rootHashfrom the log chain head; reject if it does not match the trailer value.
Failure conditions
Mark a card as tampered when any of the following are true:
- HMAC mismatch
- AES-GCM decryption failure
counterrollback detectedlastTimestamprollback detectedbalanceinconsistency withlastBalanceor first log entry- Log chain hash mismatch at any position
rootHashmismatch- Unsupported
magicorversion
Response to tamper detection
- Do not write to the card.
- Set the card status to
BLOCKED_TAMPER(code1) on the next successful authenticated write if the card is still operable. - Log the event with tamper type,
cardId,counter, andterminalIdfor backend reconciliation. - Display a generic blocked message to the user; do not expose cryptographic details.
- If the card cannot be written (e.g., read fails completely), report the event to the backend immediately.
Log chain
- Each log entry includes a 6-byte hash field.
hash[n] = SHA256(deltaTime || amount || balanceAfter || flags || hash[n-1])[0..5]- The chain is anchored at
session.startTime:hash[0]usesstartTimebytes as the initial previous value. - The trailer
rootHashstores the hash of the most recent log entry (the chain head).
Validation guarantees
- Any single-bit modification to a log entry invalidates the chain from that point forward.
rootHashin the trailer ties the entire log sequence to current card state.- Trailer HMAC binds the root hash, counter, active pointer, and metadata, preventing selective replay of a valid historical trailer with a modified payload.