Desfire Secure Channel EV2 Communication

Hi friends,

I think a number of us have started playing around with Desfire since the DF3 launch, so I am hoping someone has had more luck than I when it comes to the Secure Messaging feature - specifcally EV2. I’ve come at it from a bunch of different angles, but a huge block has been not being able to test any of the commands on my Proxmark3.

Here’s an example: AES key in plaintext because this is just a test card :man_shrugging:

[usb] pm3 → hf mfdes getuid -n 0 -t aes -k 43f416fdc5afee22017b9fae7b9c0301 --schann ev1 --cmode encrypt -a -v
[=] Key num: 0 Key algo: aes Key[16]: 43 F4 16 FD C5 AF EE 22 01 7B 9F AE 7B 9C 03 01
[=] Secure channel: n/a Command set: niso Communication mode: encrypt
[+] Setting ISODEP → inactive
[+] Setting ISODEP → NFC-A
[=] AID 000000 is selected
[=] Auth: cmd: 0xaa keynum: 0x00
[+] >>>> 90 AA 00 00 01 00 00
[+] <<<< 76 B2 13 16 11 00 8F 1F EA 91 C9 FA F8 32 09 C2 91 AF
[+] >>>> 90 AF 00 00 20 A4 3D 29 4D BF 27 BF A8 E6 71 CC 65 53 C2 0E 38 80 86 12 1B D4 8C 95 74 84 2F D8 BF 56 A8 AC 15 00
[+] <<<< B2 11 67 AE 75 41 A9 D6 7C A0 33 AE 90 65 49 08 91 00
[=] Session key : 01 02 03 04 98 45 6B C2 13 14 15 16 95 46 50 12
[=] Desfire authenticated
[+] >>>> 90 51 00 00 00
[+] <<<< B9 75 27 65 3F A8 52 3E 5F C8 7F 53 AF A8 81 8F 91 00
[+] received data[7]: 04 9A 99 22 5C 1D 90
[+] Desfire UID[7]: 04 9A 99 22 5C 1D 90
[+] Setting ISODEP → inactive`

This works in “EV1” mode but…

[usb] pm3 → hf mfdes getuid -n 0 -t aes -k 43f416fdc5afee22017b9fae7b9c0301 --schann ev2 --cmode encrypt -a -v
[=] Key num: 0 Key algo: aes Key[16]: 43 F4 16 FD C5 AF EE 22 01 7B 9F AE 7B 9C 03 01
[=] Secure channel: n/a Command set: niso Communication mode: encrypt
[+] Setting ISODEP → inactive
[+] Setting ISODEP → NFC-A
[=] AID 000000 is selected
[=] Auth first: cmd: 0x71 keynum: 0x00 key: 43 F4 16 FD C5 AF EE 22 01 7B 9F AE 7B 9C 03 01
[+] >>>> 90 71 00 00 02 00 00 00
[+] <<<< 38 75 C7 5B 6F 87 D1 17 6C 80 40 6A 3A 44 9E AA 91 AF
[+] >>>> 90 AF 00 00 20 2B BC BE 88 BD B7 A8 82 5B 6C 5C 8D 61 0D 1E 79 73 C5 D7 81 4B 66 D5 61 0D 01 4B CA DC 27 36 0B 00
[+] <<<< 67 74 08 5F 09 1F 26 B1 8F 44 42 B9 8C 6F 51 C8 C1 81 80 C1 CD F7 1B 35 3C EC 35 70 97 7D 07 AD 91 00
[=] TI : D9 05 C9 98
[=] pic : 00 00 00 00 00 00
[=] pcd : 00 00 00 00 00 00
[=] session key ENC: B7 9E 5E FA 56 A7 3B AC B2 82 5F 0E E1 C5 42 91
[=] session key MAC: 40 E6 E5 5B 5D 15 B5 0A 86 28 54 A3 0F 4A AA 9C
[=] Desfire authenticated
[+] >>>> 90 51 00 00 08 9D 1D 6E 4C 27 ED 39 97 00
[+] <<<< 91 1E
[!!] :rotating_light: APDU(9051) ERROR: [0x911E] CRC or MAC does not match data
[!!] :rotating_light: Desfire GetUID command error. Result: -20
[+] Setting ISODEP → inactive

I can run a simple command hf mfdes auth … command over ev2 but anytime auth tries to chain into another command (like the getuid) I get that CRC or MAC does not match data error. I am stumped, and the Proxmark has been my validator for anything else I’ve tried to do in code. Stupid NDA :unamused:

And yes I do recognize encrypted mode with EV1 is more than sufficient for anything I’d do, but someone’s gotta figure out how to do it - so might as well be us

2 Likes

Basically from my understanding the problem is that you need to calculate the expected MAC for each subsequent “message” and the proxmark3 is not going to do that for you. You need to write software to do that properly.

I think you might have an easier time using a more typical PCSC compliant CCID type reader like the acr1252u for example.

https://www.amazon.com/s?k=Acr1252u

1 Like

Hilariously that’s the exact device I am working with outside the Proxmark3. :rofl:
Ok that’s the answer I was afraid of but does make sense. I am giving this poor crypto library the workout of its life lol I’ll bang my head against the wall a couple more days on it, see if I get anywhere. I feel like if I could see a single example of working apdu command flow for this but afaik no one has had success at a hobbyist level.
On a semi related note: I love the LLM’s but man I just wish they’d admit when they aren’t confident on an answer. I used Claude for the first time today - let it completely redo my code. It broke everything of course, but man was it cool to watch it work. However by the end of that little experiment I swear it was changing the colour of the debug prompts from red to green to try to convince me it actually had worked lol :joy:

In this case I’m pretty sure that won’t work because it will be secure… meaning the APDUs won’t make any sense as they will be encrypted. If you tried to play them back or replicate them then that would be a replay attack and it would fail because your key exchange process would result in a different channel key from the apdus you captured.

Sounds about right hahah

2 Likes

On the off chance anyone ever comes along and wants to retread the same water as me, this GitHub already pretty much figured it out:

Stupid helpful jerk took the fun out of it by being so smart :unamused:

As I understand it, the main difference between EV1 and EV2 style is the chaining of secure components back into the session state. Since it derives new session keys for each command/response sequence based on the previous one, this adds entropy instead of reducing it over time (ChatGPT has informed me this statement is technically incorrect as it’s more about forward secrecy). The standard also requires things like CMAC verification, whereas in EV1, those are optional.

It’s super cool tech and a lot of fun to mess with - but one thing I’ve found: it’s slooooow. (Yes, I know a large part of that is using Python instead of a performant language but still…)

Since my primary purpose is a door-unlock system for now, I’m sticking with EV1-style (0x90 0xAA) authentication. Especially since there won’t be much back-and-forth between the scanner and my implant.

Still, if anyone ever wants to chat, happy to share more about what I’ve learned. My Ecko Guarantee™ is: all information is at least 13% accurate.

4 Likes