HMAC computes a keyed authentication tag over bytes so a verifier with the same secret can detect an altered message.
Python HMAC envelopes: authenticate exact bytes and separate replay policy
Operation contract
The owned fixture signs a bounded bytes payload with SHA-256 and verifies a 64-character lowercase hexadecimal tag. compare_digest compares fixed-sized raw tags. A changed payload fails verification, while the identical message/tag pair verifies again. That repeat is intentional: integrity does not make a message fresh or single-use. The fixed key is teaching material, never a deployable secret.
Failure and ownership boundary
Sign the agreed wire representation. Parsing and rebuilding equivalent JSON can change spacing or key order and therefore change the tag. Verification must be followed by a Python JSON validation: reject duplicate members and non-integer amounts, a Python SQLite job project: reject conflicting replays by request identity and an actual identity/key lifecycle. This fixture does not implement transport encryption, key rotation, expiry or recipient authorization.
Working program
import hashlib
import hmac
import re
FIXTURE_KEY = b"owned-test-key-not-a-production-secret"
def authenticated(payload, tag):
if type(payload) is not bytes or len(payload) > 128 or type(tag) is not str or re.fullmatch(r"[0-9a-f]{64}", tag) is None:
return False
expected = hmac.new(FIXTURE_KEY, payload, hashlib.sha256).digest()
return hmac.compare_digest(expected, bytes.fromhex(tag))
payload = b'{"receipt_id":"R-0041","amount":125}'
tag = hmac.new(FIXTURE_KEY, payload, hashlib.sha256).hexdigest()
print("original:", authenticated(payload, tag))
print("changed:", authenticated(payload.replace(b"125", b"250"), tag))
print("replay:", authenticated(payload, tag))
print("malformed:", authenticated(payload, "not-a-tag"))Output
original: True
changed: False
replay: True
malformed: FalseCosts and limits
Tag calculation processes O(n) payload bytes with fixed algorithm state. compare_digest concerns tag comparison, not a claim that every branch of the complete verifier has identical timing. Reception, secret storage and replay tracking have separate resource and security costs.
Common Mistakes
- An authenticated duplicate can still be a replay.
- Do not ship a fixture key or log a production key.
Connected lessons
Python JSON validation: reject duplicate members and non-integer amounts, Python strict Base64: reject alternate spellings before decoding a record, Python SQLite job project: reject conflicting replays by request identity.
Follow the ownership and update boundary
Python artifact hashes: verify owned bytes against a separately trusted expectation.
