In 2019, while working on one another EMV security research project, one of the Payment Village members found a strange little treasure on the internet: https://www.terminalsimulator.com/. It contained a Windows-based terminal and host emulator for chip/EMV/NFC transactions. The surprising part was not just that it existed, but that the EMV core was implemented in plain, unobfuscated JavaScript. Yes, really. We quickly acquired that and kept using that codebase in research for years.
Why? Because every time you want to test a theory around ARQC, ARPC, EMV card risk management, contactless behaviour, or issuer decisioning, you hit the same wall: rebuilding EMV cryptography properly means reading and contemplating thousands of pages of EMVCo specifications and then translating them into something executable without making a silent mistake halfway through.
At Payment Village, we have implemented chip transactions in our CTFs multiple times during DEF CON:
- How to Solve the DEF CON Card Hacking Challenge
And let me tell you, I dreaded the EMV crypto each and every time, so I kept deferring to that code again and again.
Last year, I needed some automation, so I revisited the idea of automating the tool. My first, less ambitious goal was to automate the host, pass card details into it, and scrape the "correct ARQC" conveniently reflected in the host output back out of the logs.
But then the obvious thought arrived: nobody is stopping me from proper vibecoding. AT LAST, I decided to outsource and open-source it!
So I took the old JavaScript issuer simulator, the sample transaction sender, and the logs, and asked Codex to rebuild the cryptogram path into a clean Python tool and a structured spec.
What the code was actually doing
Read MC-SPEC.md
The Python tool:
- accepts HSM, terminal, and card inputs as structured JSON
- generates ARQC
I ran a few tests on the data I had to verify that it's working correctly, but at the end of the day, it's vibecoded, so it could be incorrect. Use at your own discretion.
Why did this take so long to externalise
Because EMV is not one spec. It is a family of specifications, edge cases, historical behaviours, network rules, and issuer interpretations stacked on top of one another. If you want to write your own implementation or just understand the moving pieces, these references are useful starting points:
- [Customer Interface Specification](https://www.scribd.com/doc/45861572/Customer-Interface-Specification)
- [Master Derive Session Key](https://www.scribd.com/document/686241548/master-Derive-Session-Key)
- [Master Key Derivation](https://www.scribd.com/document/325688735/08-1-Master-Key-Derivation#:~:text=The%20Master%20Key%20Derivation%20method,ZR%20is%208%2Dbyte%20length)
- [EMV v4.2 Book 2: Security and Key Management](https://www.scribd.com/document/58289625/book2-EMV-v4-2-Book-2-Security-and-Key-Management-CR05-20090122094212)
Information should be free
We are sharing the code and the model because this stuff should be understandable. That does not mean "go commit fraud". It means defenders, assessors, and researchers should be able to reason about how issuer-side EMV validation actually works.
This is useful for:
- payment red team exercises
- issuer validation reviews
- terminal/host consistency testing
- CTF design
- protocol misuse research
Do not do harm. Do not commit fraud. Use it to understand where the assumptions are weak.
Immediate security lessons that jump out
Finally, I asked Codex to look at the obvious security flaws in the specs/code, and a few things immediately pop up:
- `DE22` / POS entry mode is not part of the ARQC formula. The ARQC is bound to `PAN+PSN`, `ATC`, `UN`, amounts, country/currency, `TVR`, `AIP`, `CVR`, and optional counters from `9F10`. So if contact and contactless use the same `IMK_AC` domain and the issuer does not enforce channel-specific consistency checks, the same cryptogram can be accepted across channels.
- Some DE55 fields are not cryptogram-bound either, like `9F34`, `9F27`, `9F33`, `9F35`, `9F1E`, `9F53`, `84` and `9F09`. That means they may look “chip-authenticated” to a casual reader when they are not.
- `9F27` is decision-critical but not ARQC-critical. The code separately uses `9F27` to decide approval/decline behavior, and cryptogram validity alone is not enough to conclude the transaction semantics are valid.
Back in the day, we found and wrote about some of these issues ourselves.
If logic is sloppy, a rejection cryptogram (`AAC`) can be treated as if it were acceptable, because `9F27` itself is not part of the ARQC calculation path - Modern EMV and NFC Cardholder Verification Issues
2. If a cryptogram is obtained via NFC, and issuer-side logic does not bind the transaction context tightly enough, it may be replayed into a chip/contact authorisation path, or vice versa. That kind of mismatch can be operationally useful for bypassing cumulative-limit assumptions, including PSD2-style controls - Card fraud in a PSD2 world
I must admit that LLMs and vibecoding allowed millions of people to widen their ambitions and try to build/hack/comprehend something that would not have been even possible a few years ago. And it took me longer to write this article than to write the code itself!