Every tap-to-pay transaction never sends the real card/account number over NFC. Instead the phone's Secure Element holds a device account key DK, issued once, and a counter โ the Application Transaction Counter (ATC) โ that only ever increases. Each tap derives a fresh, single-use Application Cryptogram (AC):
AC = trunc32( MAC( DK, token โ ATC โ amount โ unpredictable_number ) )
ATC โ ATC + 1 (strictly monotonic, never reused)
Terminal accepts a tap only if:
1) recomputed MAC matches the received AC, AND
2) received ATC > last ATC the issuer has seen for this device
Rule 2 is what defeats replay: an attacker who skims the radio traffic of one tap captures a cryptogram that is cryptographically valid โ but tied to that exact ATC. Presenting it again produces a value with the same ATC as one already spent, so the issuer's monotonicity check rejects it outright, even though the MAC itself still checks out. This is why a captured NFC tap, unlike a captured static card number, is worthless for a second charge.
- Tap to Pay โ increments the ATC, derives a new AC from (token, ATC, amount, nonce) with a toy MAC standing in for AES-CMAC, and sends it to the terminal.
- Amount โ feeds into the MAC input, so even two taps at the identical ATC would still diverge if amount changed (illustrative โ ATC reuse is rejected before amount is even compared).
- Replay Last Cryptogram โ attacker resends the most recently captured (token, ATC, AC) triple unmodified; the terminal's ATC monotonicity check rejects it every time.
- Reset Session โ issues a brand-new device key and zeroes the ATC, simulating re-provisioning the wallet (e.g. after adding the card on a new phone).
Real-world relevance: this ATC + per-transaction MAC scheme is the EMV Contactless / NFC layer under Apple Pay, Google Pay and Samsung Pay โ it is precisely why "sniffing" an NFC tap does not let an attacker replay a charge, unlike skimming a magnetic stripe.