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

nfc: add basic Mifare DESFire support #1024

Merged
merged 17 commits into from Mar 23, 2022
Merged

Conversation

kevinwallace
Copy link
Contributor

@kevinwallace kevinwallace commented Mar 14, 2022

What's new

  • Detection of DESFire cards based on ATQA/SAK
  • Reading of all non-protected card data
  • UI to browse apps/files in card data
  • Fixes to TextBox wrap behavior
  • Adds bool type to flipper_format
  • Saving and loading DESFire tag data

Verification

  • Open NFC app and scan a DESFire card. Here is an example of me scanning an ORCA (Seattle public transit) card and briefly browsing its data.

flipper_desfire_orca

flipper_desfire_demo_480p.mov

Checklist (do not modify)

  • PR has description of feature/bug or link to Confluence/Jira task
  • Description contains actions to verify feature/bugfix
  • I've built this code, uploaded it to the device and verified feature/bugfix

Kevin Wallace added 2 commits March 14, 2022 15:35
* Wrap width is 120 pixels, not 140. (140 is larger than the screen!)
* Glyph width already includes spacing; don't add 1 additional px
* When starting a new line, include wrapped glyph width in new line_width.
* Call canvas_set_font before text_box_insert_endline so that glyph
  width is calculated using correct font.

Previous approach worked somewhat well using default TextBoxFontText but
this version is more robust, particularly when using TextBoxFontHex.
Kevin Wallace added 2 commits March 15, 2022 16:44
/project/applications/desktop/scenes/desktop_scene_main.c:51:13: error: 'desktop_switch_to_app' defined but not used [-Werror=unused-function]
   51 | static void desktop_switch_to_app(Desktop* desktop, const FlipperApplication* flipper_app) {
      |             ^~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
make[1]: *** [/project/make/rules.mk:70: .obj/f7//project/applications/desktop/scenes/desktop_scene_main.o] Error 1

(Discovered when turning off all the other APP_*s to free up enough space to use APP_UNIT_TESTS)
Built with:
  make APP_UNIT_TESTS=1 SRV_DESKTOP=0 APP_ARCHIVE=0 APP_GPIO=0 APP_IBUTTON=0 APP_INFRARED=0 APP_LF_RFID=0 APP_NFC=0 APP_SUBGHZ=0 APP_ABOUT=0 APP_PASSPORT=0 APP_MUSIC_PLAYER=0 APP_SNAKE_GAME=0 APP_ACCESSOR=0 APP_BLINK=0 APP_INFRARED_MONITOR=0 APP_KEYPAD_TEST=0 APP_SD_TEST=0 APP_VIBRO_TEST=0 APP_USB_TEST=0 APP_DISPLAY_TEST=0 APP_BLE_HID=0 APP_USB_MOUSE=0 APP_BAD_USB=0 APP_U2F=0 APP_UART_ECHO=0
and passed `unit_tests` via cli
@kevinwallace
Copy link
Contributor Author

Updated to include saving/loading of DESFire tag data. Let me know if the flipper_format bool change is unwanted -- I'm happy to drop that commit and rewrite to use hex for those fields instead.

@skotopes
Copy link
Member

Hi @kevinwallace

Great amazing job, you are awesome! )

Adding bool is ok. @gornekich will review PR and provide feedback.

All of the same information is present in NfcSceneReadMifareDesfireSuccess,
except for ATQA/SAK, which is implicitly known for all saved DESFire cards
(0x4403, 0x20).
@kevinwallace
Copy link
Contributor Author

UI has changed a bit since the original video, here's the current state of things

desfire_ui_714915e.mov

(and thank you for Flipper -- this has been such a fun device to hack on! :)

Copy link
Member

@gornekich gornekich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @kevinwallace !
Thank you very much for your amazing work!
So far, I looked through your code and left a few comments. I need more time to dive into your DESFire implementation more carefully, and make detailed review.
I noticed a memory leakage after I open DESFire card and then exit NFC application. You can see this from our log console (LoaderSrv logs) or executing 'free' CLI command before and after launching NFC app and opening DESFire card. Memory leakage is a blocking issue for merging your pull request.
I suggest you fix memory issues while I will make detailed review in next couple of days.

@@ -163,6 +163,8 @@ int32_t nfc_app(void* p) {
if((*args != '\0') && nfc_device_load(nfc->dev, p)) {
if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl);
} else if(nfc->dev->format == NfcDeviceSaveFormatMifareDesfire) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareDesfireSuccess);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now we start nfc application with parameters if we want to launch NFC app from Archive and start UID or Mifare Ultralight emulation immediately. In your case, we will see information about selected DESFire card. I am not sure that we want to break user's expectation here, so I would delete this for now

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! 8b85ff3

continue;
}
if(!mf_df_parse_read_data_response(rx_buff, rx_len, file)) {
FURI_LOG_W(TAG, "Bad response reading file %d\r\n", file->id);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need in \r\n here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops! This was originally a printf. Done. ff1fcbf

MifareDesfireApplication* app = nfc_scene_mifare_desfire_app_get_app(nfc);
if(!app) {
FURI_LOG_E(TAG, "Bad state. No app selected?");
return;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a risk of undesired behavior. If you return here, view dispatcher will display and deliver events to the view from previous scene and execute on_event() handler of MifareDesfireApp scene. I suggest switching to a Popup view with error explanation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this codepath ever runs, it's a bug in the MifareDesfireData scene. But just in case I made a nice popup anyway. :) f5dbd6f

error_popup

@kevinwallace
Copy link
Contributor Author

Thank you for the comments!

A few hours ago, I noticed a memory leak while reviewing this PR's diff and fixed it in 56ab13e. I think this was the same leak you saw -- I'm now checking with the free CLI command and Free heap size is remaining at the same size whenever I return to the desktop from the NFC app. Could you let me know if you're still seeing this, and if there's a particular set of interactions to reproduce it?

Copy link
Member

@gornekich gornekich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect! The leakage I mentioned is gone. Also, thanks for following my suggestions!

Another issue I found is that if I go to saved DESFire card -> Emulate UID -> Back -> Info, there is no info about saved card and after I exit NFC ~3kB of heap leak.
Current UID emulation supports displaying reader commands on the screen. The reason behind this is that we give users an opportunity to research what kind of reader in front of them. From technical point of view, we pass commands received in nfc worker to emulate uid scene through NfcReaderRequestData structure in NfcDeviceData, which shares memory with MifareDesfireData. That's where after UID emulation we loose DESFire data and can't free allocated memory.
The quickest fix here is to disable UID emulation for DESFire cards, just like it's done for Bank Cards. However, if you want to keep opportunity for UID emulation, you should fix NfcDeviceData structure so that NfcReaderRequestData and MifareDesfireData don't share same memory.

Let me know how you want to fix UID emulation issue and when it's done, we will merge your brilliant PR :)

@@ -62,7 +74,11 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneDelete);
consumed = true;
} else if(event.event == SubmenuIndexInfo) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo);
if(nfc->dev->format == NfcDeviceSaveFormatMifareDesfire) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareDesfireSuccess);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to show DESFire cards Info in NfcSceneDeviceInfo, or make another NfcSceneMifareDesfireInfo scene. If I go to NfcSceneReadMifareDesfireSuccess scene from Saved cards, I don't expect to Name and save card that I already opened

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed that offering "Name and save" for an already-saved card is inappropriate. I've added support for DESFire cards to NfcSceneDeviceInfo and now go there instead of NfcSceneReadMifareDesfireSuccess when reading a saved card. The "Data" button goes to NfcSceneMifareDesfireData, and NfcSceneMifareDesfireMenu is now only accessible when scanning a new card.

bf64326

device_info

I also tried another approach, keeping NfcSceneReadMifareDesfireSuccess in both cases, and showing the 'More' button selectively based on scene history. It's in my desfire_wip branch (kevinwallace@26ef6e0, kevinwallace@a42c938) if you'd like to take a look, but after trying both, I prefer the approach in this PR.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like current approach with adding DESFire to NfcSceneDeviceInfo scene

Kevin Wallace added 2 commits March 18, 2022 10:10
This makes it safe to emulate DESFire/EMV without clobbering card data.
@kevinwallace
Copy link
Contributor Author

Good catch on the broken UID emulation behavior due to memory sharing -- I didn't look closely at the UID emulation code earlier to understand this was going on. It seems useful to still be able to emulate UID only, so I've moved NfcReaderRequestData out of the union so that it no longer shares memory with DESFire data. This means it's possible to safely emulate UID of EMV cards from the saved card menu, too, so I've also re-enabled it for those.

fcc1bfa

I'm also fine disabling it and leaving NfcReaderRequestData in the union if you prefer that approach -- let me know and I'll revise.

This can happen when a file doesn't allow unauthenticated reads (see the
call to mf_df_parse_read_data_response in nfc_worker.c).
@gornekich
Copy link
Member

Thanks for fixing UID emulation. Now everything looks good!

@skotopes skotopes merged commit 3857cd7 into flipperdevices:dev Mar 23, 2022
@gornekich
Copy link
Member

Thanks for contribution!

@kevinwallace kevinwallace deleted the desfire branch March 23, 2022 21:13
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 this pull request may close these issues.

None yet

3 participants