Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix APDU length in emvcore - excessive Lc with no data packet #171

Closed
jslawek opened this issue Apr 20, 2019 · 10 comments · Fixed by #253
Closed

Fix APDU length in emvcore - excessive Lc with no data packet #171

jslawek opened this issue Apr 20, 2019 · 10 comments · Fixed by #253

Comments

@jslawek
Copy link

jslawek commented Apr 20, 2019

Hi,

the EMV APDUs with no data (just headers) are incorrectly generated with excessive Lc byte.

According to specification, APDU consists of:

  • mandatory header: CLA, INS, P1, P2 (4 bytes)
  • conditional body: Lc (len of data), Data, Le (expected len of response)

There are 4 possible cases for APDU packet:

  1. no command data, no response needed - just 4 bytes of mandatory header
  2. no command data, response required - 4 bytes header + Le (5 bytes total)
  3. command data, no response required - 4 bytes header + Lc + Data (variable len)
  4. command data, response required - 4 bytes header + Lc + Data + Le (variable len)

In case there is no data, the emvcore generates APDU packet with excessive Lc, thus making it one byte (0x00) longer, and then adds Le at the end. For example, an APDU "read first record from SFI" generated in emvcore:

CLA INS P1 P2 Lc Le
00 B2 01 0C 00 00

but should be

CLA INS P1 P2 Le
00 B2 01 0C 00

Many cards process this one byte longer, incorrect APDU, but there are some that respond with 0x6F00 and the EMV exchange communication stops.

I think the easiest fix would be to take EMVExchange approach from the main repo:
https://github.com/Proxmark/proxmark3/blob/master/client/emv/emvcore.c#L368-L374
In case the APDU is 5 byte len, it is assumed the Le is there already, and the code generates correct len APDUs. Current RRG repo code adds the Le anyway and makes it 6 bytes incorrectly.
By the way, the EMVExchange call to EMVExchangeEx has a typo anyway - the fifth parameter should not be channel (it is the first parameter), but rather IncludeLe bool:

    return EMVExchangeEx(channel, false, LeaveFieldON, apdu, (channel == ECC_CONTACTLESS), Result, MaxResultLen, ResultLen, sw, tlv);
@merlokk
Copy link
Contributor

merlokk commented Apr 20, 2019

sometimes it true, sometimes not.

commands on different cards and via different channels needs to have different apdu sructure and sometimes it not as iso said.

in real life terminals have different "kernels" that make APDUs with a their hardcoded way.
pm3 tried to guess the way. if we try some general card - this will work (maybe like 98% of cards)
there are many points in code that checks errors and reissue commands or issue commands with EMVExchangeEx(). If you look deep - almost all the code uses guessing, not only EMVExchange().

@merlokk
Copy link
Contributor

merlokk commented Apr 20, 2019

P.S. I have tried bank certification device on pm3 and it passes all public part several most commonly used card's standard.

@merlokk
Copy link
Contributor

merlokk commented Apr 20, 2019

devnied/EMV-NFC-Paycard-Enrollment#17
American Express Kernel '04'
Discover Kernel '06'
JCB Kernel '05'
MasterCard Kernel '02'
UnionPay Kernel '07'
Visa Kernel '03'
girocard Kernel '2A'

I have not remembered from where i have copied my list. so i cant share it (im not sure if it can be shared)
but i found this) it not full but in general it enough)

@jslawek
Copy link
Author

jslawek commented Apr 20, 2019

Hi, thanks for such quick response.
Yes, I have read the code and noticed these many "checks" and "guesses". I filed the issue because in my specific case they do not work with RRG repo client, but the official repo client does work.
My case is MasterCard, however not a plastic card but mobile contactless payment (Android Host Card Emulation). When I try to execute EMV, reading records from AFL fails and the execution stops:

pm3 --> emv exec -sAT
[=] Channel: CONTACTLESS
(...)
* Read records from AFL.
* * SFI[01] start:01 end:01 offline count:00
* * * SFI[01] 1
[+] >>>> 00 B2 01 0C 00 00
[+] <<<< 6F 00
[!!] APDU(00b2) ERROR: [6F00] Command aborted - more exact diagnosis not possible (e.g., operating system error).
[!] Error SFI[01]. APDU error 6f00

There is this 6-byte APDU for read record.

I noticed one of these "checks" introduced in EMVReadRecord - "trying to reissue command without Le":
https://github.com/RfidResearchGroup/proxmark3/blob/master/client/emv/emvcore.c#L614-L617

    if (*sw == 0x6700) {
        PrintAndLogEx(INFO, ">>> trying to reissue command withouth Le...");
        res = EMVExchangeEx(channel, false, LeaveFieldON, (sAPDU) {0x00, 0xb2, SFIrec, (SFI << 3) | 0x04, 0, NULL}, false, Result, MaxResultLen, ResultLen, sw, tlv);
}

that workaround would work in my case - the resulting APDU will be 5 bytes, the 5th Lc will be treated as Le by card. But as far as I understand the execution does not reach this point because the exception is raised before.

I checked my reader (Sumup) against the same HCE card. It sends 5 bytes APDU for reading AFL, and it works.

Also the same setup but with official repo client works in my case (5 byte APDU):

* Read records from AFL.          
* * SFI[01] start:01 end:01 offline count:00          
* * * SFI[01] 1          
 >>>> 00 b2 01 0c 00           
 <<<< 70 72 9f 6c 02 00 01 (...)

And executing the commands by hand in RRG repo client works as well:

pm3 --> emv select -satk a0 00 00 00 04 10 10         
[=] Channel: CONTACTLESS 
(...)
pm3 --> hf 14a apdu -tk 00 B2 01 14 00                
>>>>[keep TLV] 00 B2 01 14 00           
<<<< 70 81 A3 57 13 55  (...)

@merlokk
Copy link
Contributor

merlokk commented Apr 20, 2019

maybe ill add 6F00 error to list. ill think

@jslawek
Copy link
Author

jslawek commented Apr 21, 2019

Oh, I have missed the fact it is 0x67 not not 0x6F...

I have added 0x6F00 here to EMVReadRecord:

if (*sw == 0x6700 || *sw == 0x6F00) {

and that did the trick for my case:

* * SFI[02] start:01 end:01 offline count:01          
* * * SFI[02] 1          
[+] >>>> 00 B2 01 14 00 00           
[+] <<<< 6F 00           
[!!] APDU(00b2) ERROR: [6F00] Command aborted - more exact diagnosis not possible (e.g., operating system error).          
[=] >>> trying to reissue command withouth Le...          
[+] >>>> 00 B2 01 14 00           
[+] <<<< 70 81 A3 57 13 55 73 (...)

But that is not really elegant solution in my opinion, also may be a bit misguiding in future - there is actually Lc which is treated as Le by the card in practice...
And I still think it would be better to comply with the standard in the first place by default, and then add exceptions for the cases that deviate from it. Now it works the other way around - by default emvcore creates packets that are not strictly compliant to standard.
Are you sure most cards do not respond here to these 5 bytes and need 6?
Like previously mentioned, my reader sends here 5. I do not have possibility to check other readers at the moment...

@merlokk merlokk mentioned this issue Jul 16, 2019
@merlokk
Copy link
Contributor

merlokk commented Jul 16, 2019

looks it done, thanks

@iceman1001
Copy link
Collaborator

Lets see what @jslawek says

@jslawek
Copy link
Author

jslawek commented Jul 16, 2019

It works, thanks!
Now the APDUs have correct length and my cards do not respond with errors, also no more "trying to reissue command without Le". It is way more elegant solution than adding another workaround for 0x67 / 0x6F.

@iceman1001
Copy link
Collaborator

EMV search leaves the rf field on afterwards, even if it doesnt find a tag

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants