Skillversitas

Message Authentication with HMAC

A plain hash proves that data has not changed, but anyone can recompute it and forge a new pair. A MAC (message authentication code) adds a secret key to the hash so that only someone holding the key can produce a valid code. HMAC - hash-based MAC - is the standard construction: it applies a keyed hash (SHA-256 in practice) in a specific two-pass pattern over the message and the key, and it is available in the standard library.

Sign and Verify

import hashlib
import hmac
import secrets

key = secrets.token_bytes(32)          # shared secret, 256 bits
message = b"transfer 1000 USD to bob"

def sign(msg: bytes) -> bytes:
    return hmac.new(key, msg, hashlib.sha256).digest()

def verify(msg: bytes, mac: bytes) -> bool:
    expected = hmac.new(key, msg, hashlib.sha256).digest()
    return hmac.compare_digest(expected, mac)

mac = sign(message)
print(verify(message, mac))            # True
print(verify(message + b" ", mac))     # False - one byte changed

compare_digest is mandatory: a regular == can short-circuit on the first differing byte and leak information through timing. The MAC travels with the message (header, footer, or side channel) and the verifier recomputes and compares.

Encoding for Transport

import base64

token = base64.urlsafe_b64encode(mac).decode()
# "k9xQ...=" - safe to put in a URL, header, or JSON field

mac_back = base64.urlsafe_b64decode(token.encode())

This exact pattern - payload plus base64 MAC - is what JWTs do in their header.payload.signature structure, and what webhooks use to prove that a POST came from the provider.

The cryptography Library Variant

from cryptography.hazmat.primitives import hmac as chmac
from cryptography.hazmat.primitives import hashes

h = chmac.HMAC(hashlib_sha256 := hashes.SHA256(), key)
h.update(message)
mac = h.finalize()

verifier = chmac.HMAC(hashes.SHA256(), key)
verifier.update(message)
try:
    verifier.finalize(mac)     # raises InvalidTag if the MAC does not match
    print("authenticated")
except Exception:
    print("tampered")

HMAC vs Digital Signature

HMACRSA/Ed25519 signature
Secret sharingkey is shared (symmetric)only signer holds the private key
Third-party verificationno (they need the key)yes (public key)
Speedvery fastslower
Typical useAPI webhooks, cache entries, DB row integritysoftware updates, certificates, contracts

Use HMAC when both sides already share a secret; use a real digital signature (next RSA chapters) when the verifier is a stranger or when you need non-repudiation.

Summary

Compute MACs with hmac.new(key, msg, sha256), transmit them in a stable encoding like base64url, and always verify with compare_digest. HMAC is the cheapest way to make "this exact byte string was approved by the key holder" true between two parties who share that key.