DUKPT for Android developers: why every card tap gets its own key

Move from consumer Android apps onto a POS payments app and the first thing that trips you up isn’t Android. It’s the alphabet soup around card security: BDK, IPEK, KSN, DUKPT. None of it is hard once someone draws the picture, so here is the picture.

The problem DUKPT solves

A POS terminal reads a card PIN or card data and has to send it, encrypted, to a payment processor. The obvious approach is one fixed secret key per terminal. The trouble is that terminals sit on shop counters. They get stolen, opened and probed. If an attacker extracts that one key, every transaction the terminal has ever sent with it, and will ever send, is exposed.

DUKPT (Derived Unique Key Per Transaction, defined in the ANSI X9.24 standard) fixes that by giving every single transaction its own encryption key, while the processor only has to protect one master key for a whole fleet of terminals.

The three pieces

BDK: Base Derivation Key. The master secret. It lives only inside hardware security modules (HSMs) at the processor and the key-injection facility, and it is never stored on a terminal. One BDK can serve a very large fleet of terminals.

KSN: Key Serial Number. A value sent in the clear with every transaction. It identifies which BDK was used, which device sent the transaction, and carries a transaction counter that advances with every transaction.

IPEK: Initial PIN Encryption Key (newer documents call it the Initial Key). It is derived from the BDK and the terminal’s initial KSN, then injected into the terminal at a secure facility. It is the only key the terminal is born with.

How a transaction key is born

  1. At injection, the terminal uses its IPEK to fill a small set of “future key” registers, then erases the IPEK itself.
  2. For each transaction it takes the future key that matches the current counter, derives the working key from it and encrypts the PIN or card data. It sends the ciphertext plus the KSN.
  3. Used keys are erased and replaced by new future keys derived through one-way functions, so there is no way to walk backwards from today’s keys to old ones.
  4. The processor reads the KSN, uses the BDK inside its HSM to re-derive exactly the same transaction key, and decrypts.

The terminal never holds the BDK. A leaked transaction key unlocks only that one transaction, and keys pulled out of a terminal today can’t decrypt the transactions it sent before.

What this means for you as the Android developer

You will almost never implement the derivation maths yourself. On a payments terminal it runs inside the secure processor or the vendor’s PIN pad SDK. What you do own:

  • Handling the KSN correctly. It travels with every request and must reach the backend untouched. A truncated or reformatted KSN makes decryption fail, which looks like random declines.
  • Never logging what you shouldn’t. KSNs are generally safe to log and invaluable for debugging. PINs, PIN blocks, full card numbers, track data and keys never are. Make your logging layer enforce this; don’t rely on code review.
  • Planning for key changes. Terminals get re-keyed, BDKs get rotated and algorithms get upgraded, for example from Triple DES to AES, which X9.24-3 defines for DUKPT. Treat the key scheme as a version the app can migrate with a rollout plan, not a constant.
  • Testing with test keys on real hardware. Payment vendors and processors provide test BDKs and test cards. Use them on real devices early; an emulator can’t exercise the terminal’s secure hardware.

The one-line summary

DUKPT lets a processor protect one master key while every card tap on every terminal is encrypted with a key that is used exactly once. As an Android developer your job is to carry the KSN faithfully, keep secrets out of logs and treat key schemes as something you will migrate one day.

Get the next one