Wiki

Clone wiki

nfc-emv / Home

Reverse engineering of contactless NFC-EMV payments

I wondered for a long time how a contactless NFC payment is designed and how hard would it be to mount an attack such as payment without card presence or replay attack.

How did I decode the communication?

I MitM-ed communication between my card and NFC payment terminal. Part of the decoding was done from public EMV specs (they are huuuuge). Other is reversing and guesswork.

There is much more than what is shown here, the protocols are big. All transactions were PIN-less (under limit thus no PIN required). It should be noted that every card may have one or more applications and they may behave differently.

So what are the terminal and card saying to each other?

The protocol has lot of layers, the physical part is ISO 14443, on top of which are the ISO 7816 smartcard APDUs sent.

APDUs are something like "assembler" for smartcards - SIM cards use it, too. Parts of the protocol is standardized, which means a few common commands like file and application selection are well-known. The rest is proprietary, but not always unknown.

Command APDU is at usually at least 5 bytes long and has this structure:

  • CLA - class of instruction (changes among smartcard types)
  • INS - the actual instruction byte
  • P1 - parameter byte 1
  • P2 - parameter byte 2
  • Lc - length of data in this instruction
  • Data - optional data bytes
  • Le - optional amount of bytes expected in answer

In reality there are many exceptions to standard APDU form, but no need to go into them here.

For instance, SELECT instruction employs CLA 0x00 and INS 0xA4 while GET PROCESSING OPTIONS has CLA 0x80 and INS 0xA8. CLA 0x00 is used for standard ISO 7816 instructions while CLA 0x80 is used for proprietary ones.

Response to command APDU has just data and two status bytes, SW1 and SW2 that tell what result of operation was. ASN.1 is used extensively when encoding objects. However, these structures are fairly different than X.509.

Visa payment

So, without much ado, here are the decoded instructions, overview first:

1. terminal: SELECT application ID "2PAY.SYS.DDF01"
2. card: returns file control information for VISA application
3. terminal: SELECT application ID for VISA application
4. card: returns file control information containing PDOL (Processing Data Options List)
5. terminal: GET PROCESSING OPTIONS (this is the actual payment command)
6. card: returns application data (including digital signature/MAC)

I parsed all the parts of the above instructions, but we'll focus on two important part of payment process: the PDOL and GET PROCESSING OPTIONS (GPO for short).

PDOL basically says what data the terminal needs to send to the card so that the payment can proceed and what data is signed by the card in response.

The terminal sends the GPO instruction 80 A8 00 00 followed by data length and data according to what PDOL requires.

So here's the parsed PDOL from step 4:

Size Field Tag
4 Terminal Transaction Qualifiers 9F66
6 Amount, Authorized 9F02
6 Amount, Other 9F03
2 Terminal Country Code 9F1A
5 Terminal Verification Result 95
2 Transaction Currency Code 5F2A
3 Transaction Date 9A
1 Transaction Type 9C
4 Unpredictable Number 9F37

The above table describes what data are sent in GET PROCESSING OPTIONS command in step 5 to the card.

Field description

Terminal Transaction Qualifiers inform the card what authentication methods are supported by terminal. "Amount, Other" was the actual amount for payment, the "Amount, Authorized" was some strange number.

Terminal Country Code, Transaction Currency Code and Date are mostly self-explanatory. Terminal Verification Result was always zero, because the APDU exchange occured before terminal could do any verification (I think they do it because it makes the payment seem really fast).

Unpredictable Number is a very important part that makes basic replay attack impossible. Note that the nonce only 32-bit. Coupled with birthday paradox that could make some attack almost practical.

When I sniffed the actual GPO command serveral times, it has some two bogus extra zero bytes just after "Amount, Other", not sure what exactly they were.

Visa card response to GET PROCESSING OPTIONS

Size Field Tag
18 Issuer Application Data 9F10
2 Cardholder Name 5F20
19 Track 2 Equivalent Data 57
1 Application Primary Account Number 5F34
2 Application Interchange Profile 82
2 Application Transaction Counter 9F36
8 Application Cryptogram 9F26
2 Card Transaction Qualifiers 9F6C

There are few noticeable things in the response:

  • Issuer Application Data is proprietary, but I can clearly see that there's 32-bit nonce since it changes among payments
  • Cardholder Name is actually only 2 bogus data bytes
  • contents of magstripe Track 2 is copied and sent over the air in plaintext (contains card number for instance)
  • Signature or MAC is represented by Application Cryptogram. It's only 64 bits for some strange reason

I can't say why the application cryptogram is so short - either it's truncated for some obscure reason or DES is used for MAC (unlikely). The truncation case doesn't make much sense since the card has to compute a wider cipher anyway. Can it be a case of "homegrown crypto"?

Mastercard payment

Mastercard is in some ways similar, but the payment data itself clearly indicate that the card uses asymmetric cryptography in signatures. This makes the data messages much longer.

Also, there are two PDOLs, called CDOL1 and CDOL2 (Card Risk Management Data Object List). I'd guess that one is for online use, while other is for offline use.

The instruction sequence for Mastercard payment:

1. terminal: SELECT application ID "2PAY.SYS.DDF01"
2. card: returns file control information for Mastercard application
3. terminal: SELECT application ID for Mastercard application
4. card: returns file control information for Mastercard sub-application
5. terminal: GET PROCESSING OPTIONS - data is almost empty!
6. card: returns application file locator (??)
7. terminal: READ RECORD 0x0114 - PDOL
8. card: returns CDOL1 and CDOL2
9. terminal: READ RECORD 0x011c - issuer certificate
10. card: returns issuer's RSA pubkey and certificate
11. terminal: READ RECORD 0x0124 - card other public key data
12. card: returns card's RSA remainder and exponent
13. terminal: READ RECORD 0x0224 - card certificate
14. card: returns card's public key certificate
15. terminal: EXECUTE - this is actual payment
16. card: returns RSA signature and issuer application data

Data given to execute function, parsed:

Size Field Tag
6 Amount, Authorized 9F02
6 Amount, Other 9F03
2 Terminal Country Code 9F1A
5 Terminal Verification Result 95
2 Transaction Currency Code 5F2A
3 Transaction Date 9A
1 Transaction Type 9C
4 Unpredictable Number 9F37
1 Terminal Type 9F35
2 Data Authentication Code 9F45
8 ICC Dynamic Number 9F4C
3 Cardholder Verification Method results 9F34

Card's response to EXECUTE:

Size Field Tag
1 Cryptogram Information Data 9F27
2 Application Transaction Counter 9F36
120 Signed Dynamic Application Data 9F4B
18 Issuer Application Data 9F10

Analysis of Mastercard payment instructions

Many of the fields are similar to Visa, even the unpredictable number (nonce) from terminal is still 32-bit only. There does not seem to be nonce from card side.

Not sure what ICC Dynamic Number stands for.

I did a brief look at the RSA signature and it seems a bit too small. I don't know yet how to parse the modulus from the certificate, but signature is 120 bytes in length, ended with 12 0xFF bytes which seem like padding. Thus the RSA modulus can be at most 864 bits long - seems a little bit too low. Almost definitely factorable by a larger botnet or big cluster (768-bit RSA was factored some time ago).

Conclusion

Although no simple apparent attacks are present in what we have seen, some parameters are dangerously close to be fully comfortable - namely the signature sizes, RSA modulus size and nonce sizes.

Updated