Chapter 1: The Instrument Does Not Exist

First in a series on chip payments, the questions you cannot answer by reading, and the hardware you end up building instead.

Two numbers:

DE4    000000012000     the network reads this     →  120.00
9F02   000000000001     the card signed this       →  0.01

One transaction. One message. Two copies of the amount, and they do not agree.

That is not a photograph of a broken system. It is a photograph of a question, taken with an instrument that did not exist when I started asking it. Building that instrument is what this series is actually about.

The amount travels twice

When you pay with a chip card, the amount reaches your bank in two different places inside the same message, and each one is protected differently.

The first copy is a plain field. In ISO 8583, the language banks use to ask each other may this payment go through: it is data element 4. That is the number that gets routed, limit-checked, and approved. It is what the issuer’s authorization logic reads. It is, in every practical sense, the amount of the transaction.

The second copy is somewhere else entirely. Inside field 55 the message carries the chip’s contribution to the conversation: the cryptogram the card produced, and, echoed back alongside it, the values the terminal fed the card in order to produce it. Among those sits tag 9F02. That is the figure the card put its cryptogram over. The amount the chip actually signed.

Two copies, protected by two entirely different mechanisms, and no rule anywhere in the stack that says they have to agree.

The reason that is possible at all is structural: the card and the issuer never speak to each other. The card talks to a terminal. The terminal talks to an acquirer. The acquirer talks to a network, which talks to the issuer. Every hop trusts the summary handed to it by the previous one. The chip’s signature travels the whole distance intact, and the number that gets approved is composed at the terminal.

Where the comparison would happen, if it happened

There is one mechanism in the chain that could catch a disagreement without anyone having deliberately gone looking for one, and it is worth being precise about it, because it is exactly where this question lives.

The cryptogram is not a signature over the message. It is a signature over a specific list of values the terminal handed to the card: the amount among them. And when the issuer verifies it, it does not compare the cryptogram against anything in the message directly. It rebuilds that same list from data it holds, computes what the cryptogram should have been, and checks whether it matches what arrived.

So a comparison does happen, in a sense, automatically. The entire question is which copy of the amount goes into the rebuild.

If the issuer reconstructs using DE4, the routed amount, the one it is about to approve, then any disagreement destroys the cryptogram check. The transaction fails on cryptographic grounds, and it fails without anybody ever having written a rule about amounts, because the binding is doing the work implicitly. That is the elegant case, and it is what the design appears to intend.

If instead the issuer reconstructs using the copy that arrived alongside the cryptogram in field 55, the verification succeeds perfectly. The cryptogram is valid. It is genuinely valid, it is a real signature by a real card over a real amount. It is simply not a signature over the amount being approved.

Same standard. Same fields. Same message. Two implementations, opposite outcomes, and nothing in the specification that tells you which one you are talking to.

If the second case is real anywhere, then in that place the signature is protecting something other than the payment that just happened.

Why I could not simply look it up

Here is where I want to be careful, because this is the part that usually gets overstated.

I do not know whether this matters. That is the honest state of it. I do not know which reconstruction real switches perform. I do not know whether some issuers do it one way and others the other, whether it varies by network, by region, by whichever vendor wrote a particular authorization stack in whichever decade. Nobody publishes that. It is not the kind of thing that appears in a specification, because it is not a rule; it is a choice made inside somebody’s verification routine, and both choices look identical from outside right up until the two copies stop agreeing.

I read the specifications first. They tell you what the fields mean and where they travel. They do not tell you what any particular component does when the fields contradict each other, because from the specification’s point of view that situation is not supposed to arise.

Which leaves exactly one way to find out. You have to sit in the middle, change one of the two copies on purpose, leave everything else intact, and watch what the other side does about it.

That is not a question you can answer by reading. It is a question you can only answer by measuring. And measuring requires an instrument.

Everything downstream of that sentence is engineering

This is the part of research that nobody warns you about, and it is the reason this is a series rather than a post.

The question is one sentence long. When these two copies disagree, does anything downstream notice? It is narrow, it is testable, and it is completely obvious once you see it. Getting into a position where you can actually ask it took the overwhelming majority of the work.

Because to change one copy of the amount and not the other, you have to be inside the conversation. Not observing it — inside it. You need a real card on one side, a real terminal on the other, every byte between them passing through something you control, and the whole thing convincing enough that neither end suspects anything has changed.

You need, in other words, a relay. And then you need it to work on a bank card.

A reasonable place to start

Relaying a smart card is not a new idea, and I did not want to build one from nothing. SIMtrace2 already exists, Osmocom’s hardware and firmware for tracing and emulating SIM cards, mature, open, and doing very nearly the thing I needed. A board that presents itself to a reader as a card, down to the electrical layer: VCC, clock, reset, and a single half-duplex I/O line. A host program that forwards commands to a real card in a reader on your desk.

Point that at a bank card and a payment terminal, and it does not work.

It does not fail in an interesting way at first, either. It fails at the very beginning, before a single command is exchanged, for reasons that turn out to be stacked one behind another; each one only visible after you have cleared the one in front of it.

A SIM is a forgiving subject. It speaks T=0, the byte-oriented protocol. It answers with a short ATR. It lives inside a phone that is patient about timing.

A bank card is not a forgiving subject. A large share of them speak only T=1, the block-oriented protocol, which SIMtrace2’s card emulation did not implement at all. Their ATR encodes a clock rate, a guard time, and block parameters they genuinely expect to be honoured. And the terminal on the other side enforces EMV’s own rules on top of ISO 7816-3, and will turn a card down before exchanging a single byte of application data if what it hears falls outside them.

There was also a smaller thing, which turned out not to be smaller at all. The card emulation announced itself with a hardcoded ATR: 3B 00. Two bytes. A card with no identity and nothing to declare, a generic answer, never this card’s answer. For relaying a SIM into a phone that is a defensible simplification. For presenting a specific bank card to a terminal that has opinions about what cards are supposed to say, it is fatal.

So the first wall is not cryptographic and it is not clever. The terminal applies power, applies a clock, releases reset, listens for the card to introduce itself, does not like what it hears, and gives up. Six times, and then it deactivates the line.

The character of what follows

I want to set an expectation about the rest of this series, because it shaped how I worked and it is the most useful thing I have to pass on.

Three defects stood between a card that activated and a transaction that completed. They shared an uncomfortable property: not one of them produced an error message anywhere.

The ATR was refused with every single character transmitted correctly. Responses were freed inside the firmware without a log line. Chunks collided on a shared wire while both sides’ logs showed a flawless exchange. There was no stack trace, no exception, no red text. There was a terminal that stopped, and a log that insisted everything was fine.

Every one of them was found the same way: by reasoning about what should have been on the line and was not. And the last one only fell after a control experiment: taking the card out of the relay and putting it directly into the same terminal, moved the fault from the terminal, where I had been looking, back to my own equipment, where it belonged.

If there is a method in any of this, it is that one. Several changes I made across this work were made on plausible theories and had to be undone afterwards. What actually moved things forward was getting a fact: decoding the reset cause, dumping the USB configuration descriptors, capturing the debug UART, running the control experiment.

Prefer obtaining a fact over trying a fix.

Where chapter 2 starts

The terminal was reading the whole ATR. Every character was arriving correctly, the firmware had already worked out and printed which protocols the ATR offered. Nothing was wrong at the electrical level, nothing was wrong at the character level, and the card was being turned down anyway.

Which meant the content was being refused, and the ATR is only nineteen bytes long.

Next chapter is those nineteen bytes: what a card promises when it introduces itself, why a relay cannot honestly keep most of those promises, which two bytes everybody assumes are electrical and are not, and how nineteen bytes going in became fifteen bytes coming out.


About this series

Cadence. New chapter every two weeks.
Subscribe if you want them as they land.

On the code. The framework is not public and I have not pushed the firmware work upstream. This series describes what I built and what I learned building it; it is not a build guide, and I am not going to publish complete implementations here. That may change: the protocol work in these chapters is a genuine gap in an open project, and I would rather it ended up there than nowhere. If you maintain something this touches, or you work on the defensive side of it, my contact details are on the about page.

On scope. Everything in this series was done in a lab, with my own cards and my own equipment, against terminals and hosts I am authorized to test. No cardholder data appears anywhere in it, and none will. Where a transaction transcript is shown, identifying fields are removed.