Nfc: add basic Mifare DESFire support (#1024)
* Fix TextBox word wrap behavior * 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. * Add basic Mifare DESFire reading, file/app browser * Fix build with APP_ARCHIVE=0 * Add bool type to flipper_format * Add ability to save and load DESFire card data * Skip over NfcSceneDeviceInfo when viewing saved DESFire info * mf_df_clear: don't leak master key settings key versions * When opening a DESFire card from Archive, retain UID emulation behavior * rm unnecessary \r\n * show Popup instead of leaving view in bad state * Move NfcReaderRequestData out of union This makes it safe to emulate DESFire/EMV without clobbering card data. * Display saved DESFire cards via NfcSceneDeviceInfo * Display and save file metadata even when contents are missing 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). Co-authored-by: Kevin Wallace <git+flipperzero@kevin.wallace.seattle.wa.us> Co-authored-by: あく <alleteam@gmail.com> Co-authored-by: gornekich <n.gorbadey@gmail.com>
This commit is contained in:
		
							parent
							
								
									d075e00ae1
								
							
						
					
					
						commit
						3857cd7d5f
					
				| @ -48,6 +48,7 @@ static void desktop_scene_main_interact_animation_callback(void* context) { | |||||||
|         desktop->view_dispatcher, DesktopAnimationEventInteractAnimation); |         desktop->view_dispatcher, DesktopAnimationEventInteractAnimation); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #ifdef APP_ARCHIVE | ||||||
| static void desktop_switch_to_app(Desktop* desktop, const FlipperApplication* flipper_app) { | static void desktop_switch_to_app(Desktop* desktop, const FlipperApplication* flipper_app) { | ||||||
|     furi_assert(desktop); |     furi_assert(desktop); | ||||||
|     furi_assert(flipper_app); |     furi_assert(flipper_app); | ||||||
| @ -65,6 +66,7 @@ static void desktop_switch_to_app(Desktop* desktop, const FlipperApplication* fl | |||||||
| 
 | 
 | ||||||
|     furi_thread_start(desktop->scene_thread); |     furi_thread_start(desktop->scene_thread); | ||||||
| } | } | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
| void desktop_scene_main_callback(DesktopEvent event, void* context) { | void desktop_scene_main_callback(DesktopEvent event, void* context) { | ||||||
|     Desktop* desktop = (Desktop*)context; |     Desktop* desktop = (Desktop*)context; | ||||||
|  | |||||||
| @ -57,17 +57,18 @@ static void text_box_insert_endline(Canvas* canvas, TextBoxModel* model) { | |||||||
|     const char* str = model->text; |     const char* str = model->text; | ||||||
|     size_t line_num = 0; |     size_t line_num = 0; | ||||||
| 
 | 
 | ||||||
|     const size_t text_width = 140; |     const size_t text_width = 120; | ||||||
| 
 | 
 | ||||||
|     while(str[i] != '\0') { |     while(str[i] != '\0') { | ||||||
|         char symb = str[i++]; |         char symb = str[i++]; | ||||||
|         if(symb != '\n') { |         if(symb != '\n') { | ||||||
|             line_width += canvas_glyph_width(canvas, symb) + 1; |             size_t glyph_width = canvas_glyph_width(canvas, symb); | ||||||
|             if(line_width > text_width) { |             if(line_width + glyph_width > text_width) { | ||||||
|                 line_num++; |                 line_num++; | ||||||
|                 line_width = 0; |                 line_width = 0; | ||||||
|                 string_push_back(model->text_formatted, '\n'); |                 string_push_back(model->text_formatted, '\n'); | ||||||
|             } |             } | ||||||
|  |             line_width += glyph_width; | ||||||
|         } else { |         } else { | ||||||
|             line_num++; |             line_num++; | ||||||
|             line_width = 0; |             line_width = 0; | ||||||
| @ -94,18 +95,19 @@ static void text_box_insert_endline(Canvas* canvas, TextBoxModel* model) { | |||||||
| static void text_box_view_draw_callback(Canvas* canvas, void* _model) { | static void text_box_view_draw_callback(Canvas* canvas, void* _model) { | ||||||
|     TextBoxModel* model = _model; |     TextBoxModel* model = _model; | ||||||
| 
 | 
 | ||||||
|     if(!model->formatted) { |  | ||||||
|         text_box_insert_endline(canvas, model); |  | ||||||
|         model->formatted = true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     canvas_clear(canvas); |     canvas_clear(canvas); | ||||||
|     elements_slightly_rounded_frame(canvas, 0, 0, 124, 64); |  | ||||||
|     if(model->font == TextBoxFontText) { |     if(model->font == TextBoxFontText) { | ||||||
|         canvas_set_font(canvas, FontSecondary); |         canvas_set_font(canvas, FontSecondary); | ||||||
|     } else if(model->font == TextBoxFontHex) { |     } else if(model->font == TextBoxFontHex) { | ||||||
|         canvas_set_font(canvas, FontKeyboard); |         canvas_set_font(canvas, FontKeyboard); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     if(!model->formatted) { | ||||||
|  |         text_box_insert_endline(canvas, model); | ||||||
|  |         model->formatted = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     elements_slightly_rounded_frame(canvas, 0, 0, 124, 64); | ||||||
|     elements_multiline_text(canvas, 3, 11, model->text_pos); |     elements_multiline_text(canvas, 3, 11, model->text_pos); | ||||||
|     elements_scrollbar(canvas, model->scroll_pos, model->scroll_num); |     elements_scrollbar(canvas, model->scroll_pos, model->scroll_num); | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										396
									
								
								applications/nfc/nfc_device.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										396
									
								
								applications/nfc/nfc_device.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -16,6 +16,7 @@ NfcDevice* nfc_device_alloc() { | |||||||
| 
 | 
 | ||||||
| void nfc_device_free(NfcDevice* nfc_dev) { | void nfc_device_free(NfcDevice* nfc_dev) { | ||||||
|     furi_assert(nfc_dev); |     furi_assert(nfc_dev); | ||||||
|  |     nfc_device_clear(nfc_dev); | ||||||
|     furi_record_close("storage"); |     furi_record_close("storage"); | ||||||
|     furi_record_close("dialogs"); |     furi_record_close("dialogs"); | ||||||
|     free(nfc_dev); |     free(nfc_dev); | ||||||
| @ -28,6 +29,8 @@ void nfc_device_prepare_format_string(NfcDevice* dev, string_t format_string) { | |||||||
|         string_set_str(format_string, "Bank card"); |         string_set_str(format_string, "Bank card"); | ||||||
|     } else if(dev->format == NfcDeviceSaveFormatMifareUl) { |     } else if(dev->format == NfcDeviceSaveFormatMifareUl) { | ||||||
|         string_set_str(format_string, nfc_mf_ul_type(dev->dev_data.mf_ul_data.type, true)); |         string_set_str(format_string, nfc_mf_ul_type(dev->dev_data.mf_ul_data.type, true)); | ||||||
|  |     } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { | ||||||
|  |         string_set_str(format_string, "Mifare DESFire"); | ||||||
|     } else { |     } else { | ||||||
|         string_set_str(format_string, "Unknown"); |         string_set_str(format_string, "Unknown"); | ||||||
|     } |     } | ||||||
| @ -53,6 +56,11 @@ bool nfc_device_parse_format_string(NfcDevice* dev, string_t format_string) { | |||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |     if(string_start_with_str_p(format_string, "Mifare DESFire")) { | ||||||
|  |         dev->format = NfcDeviceSaveFormatMifareDesfire; | ||||||
|  |         dev->dev_data.nfc_data.protocol = NfcDeviceProtocolMifareDesfire; | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|     return false; |     return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -154,6 +162,383 @@ bool nfc_device_load_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) { | |||||||
|     return parsed; |     return parsed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static bool nfc_device_save_mifare_df_key_settings( | ||||||
|  |     FlipperFormat* file, | ||||||
|  |     MifareDesfireKeySettings* ks, | ||||||
|  |     const char* prefix) { | ||||||
|  |     bool saved = false; | ||||||
|  |     string_t key; | ||||||
|  |     string_init(key); | ||||||
|  | 
 | ||||||
|  |     do { | ||||||
|  |         string_printf(key, "%s Change Key ID", prefix); | ||||||
|  |         if(!flipper_format_write_hex(file, string_get_cstr(key), &ks->change_key_id, 1)) break; | ||||||
|  |         string_printf(key, "%s Config Changeable", prefix); | ||||||
|  |         if(!flipper_format_write_bool(file, string_get_cstr(key), &ks->config_changeable, 1)) | ||||||
|  |             break; | ||||||
|  |         string_printf(key, "%s Free Create Delete", prefix); | ||||||
|  |         if(!flipper_format_write_bool(file, string_get_cstr(key), &ks->free_create_delete, 1)) | ||||||
|  |             break; | ||||||
|  |         string_printf(key, "%s Free Directory List", prefix); | ||||||
|  |         if(!flipper_format_write_bool(file, string_get_cstr(key), &ks->free_directory_list, 1)) | ||||||
|  |             break; | ||||||
|  |         string_printf(key, "%s Key Changeable", prefix); | ||||||
|  |         if(!flipper_format_write_bool(file, string_get_cstr(key), &ks->master_key_changeable, 1)) | ||||||
|  |             break; | ||||||
|  |         string_printf(key, "%s Max Keys", prefix); | ||||||
|  |         if(!flipper_format_write_hex(file, string_get_cstr(key), &ks->max_keys, 1)) break; | ||||||
|  |         for(MifareDesfireKeyVersion* kv = ks->key_version_head; kv; kv = kv->next) { | ||||||
|  |             string_printf(key, "%s Key %d Version", prefix, kv->id); | ||||||
|  |             if(!flipper_format_write_hex(file, string_get_cstr(key), &kv->version, 1)) break; | ||||||
|  |         } | ||||||
|  |         saved = true; | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     string_clear(key); | ||||||
|  |     return saved; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool nfc_device_load_mifare_df_key_settings( | ||||||
|  |     FlipperFormat* file, | ||||||
|  |     MifareDesfireKeySettings* ks, | ||||||
|  |     const char* prefix) { | ||||||
|  |     bool parsed = false; | ||||||
|  |     string_t key; | ||||||
|  |     string_init(key); | ||||||
|  | 
 | ||||||
|  |     do { | ||||||
|  |         string_printf(key, "%s Change Key ID", prefix); | ||||||
|  |         if(!flipper_format_read_hex(file, string_get_cstr(key), &ks->change_key_id, 1)) break; | ||||||
|  |         string_printf(key, "%s Config Changeable", prefix); | ||||||
|  |         if(!flipper_format_read_bool(file, string_get_cstr(key), &ks->config_changeable, 1)) break; | ||||||
|  |         string_printf(key, "%s Free Create Delete", prefix); | ||||||
|  |         if(!flipper_format_read_bool(file, string_get_cstr(key), &ks->free_create_delete, 1)) | ||||||
|  |             break; | ||||||
|  |         string_printf(key, "%s Free Directory List", prefix); | ||||||
|  |         if(!flipper_format_read_bool(file, string_get_cstr(key), &ks->free_directory_list, 1)) | ||||||
|  |             break; | ||||||
|  |         string_printf(key, "%s Key Changeable", prefix); | ||||||
|  |         if(!flipper_format_read_bool(file, string_get_cstr(key), &ks->master_key_changeable, 1)) | ||||||
|  |             break; | ||||||
|  |         string_printf(key, "%s Max Keys", prefix); | ||||||
|  |         if(!flipper_format_read_hex(file, string_get_cstr(key), &ks->max_keys, 1)) break; | ||||||
|  |         MifareDesfireKeyVersion** kv_head = &ks->key_version_head; | ||||||
|  |         for(int key_id = 0; key_id < ks->max_keys; key_id++) { | ||||||
|  |             string_printf(key, "%s Key %d Version", prefix, key_id); | ||||||
|  |             uint8_t version; | ||||||
|  |             if(flipper_format_read_hex(file, string_get_cstr(key), &version, 1)) { | ||||||
|  |                 MifareDesfireKeyVersion* kv = malloc(sizeof(MifareDesfireKeyVersion)); | ||||||
|  |                 memset(kv, 0, sizeof(MifareDesfireKeyVersion)); | ||||||
|  |                 kv->id = key_id; | ||||||
|  |                 kv->version = version; | ||||||
|  |                 *kv_head = kv; | ||||||
|  |                 kv_head = &kv->next; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         parsed = true; | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     string_clear(key); | ||||||
|  |     return parsed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool nfc_device_save_mifare_df_app(FlipperFormat* file, MifareDesfireApplication* app) { | ||||||
|  |     bool saved = false; | ||||||
|  |     string_t prefix, key; | ||||||
|  |     string_init_printf(prefix, "Application %02x%02x%02x", app->id[0], app->id[1], app->id[2]); | ||||||
|  |     string_init(key); | ||||||
|  |     uint8_t* tmp = NULL; | ||||||
|  | 
 | ||||||
|  |     do { | ||||||
|  |         if(app->key_settings) { | ||||||
|  |             if(!nfc_device_save_mifare_df_key_settings( | ||||||
|  |                    file, app->key_settings, string_get_cstr(prefix))) | ||||||
|  |                 break; | ||||||
|  |         } | ||||||
|  |         uint32_t n_files = 0; | ||||||
|  |         for(MifareDesfireFile* f = app->file_head; f; f = f->next) { | ||||||
|  |             n_files++; | ||||||
|  |         } | ||||||
|  |         tmp = malloc(n_files); | ||||||
|  |         int i = 0; | ||||||
|  |         for(MifareDesfireFile* f = app->file_head; f; f = f->next) { | ||||||
|  |             tmp[i++] = f->id; | ||||||
|  |         } | ||||||
|  |         string_printf(key, "%s File IDs", string_get_cstr(prefix)); | ||||||
|  |         if(!flipper_format_write_hex(file, string_get_cstr(key), tmp, n_files)) break; | ||||||
|  |         bool saved_files = true; | ||||||
|  |         for(MifareDesfireFile* f = app->file_head; f; f = f->next) { | ||||||
|  |             saved_files = false; | ||||||
|  |             string_printf(key, "%s File %d Type", string_get_cstr(prefix), f->id); | ||||||
|  |             if(!flipper_format_write_hex(file, string_get_cstr(key), &f->type, 1)) break; | ||||||
|  |             string_printf( | ||||||
|  |                 key, "%s File %d Communication Settings", string_get_cstr(prefix), f->id); | ||||||
|  |             if(!flipper_format_write_hex(file, string_get_cstr(key), &f->comm, 1)) break; | ||||||
|  |             string_printf(key, "%s File %d Access Rights", string_get_cstr(prefix), f->id); | ||||||
|  |             if(!flipper_format_write_hex( | ||||||
|  |                    file, string_get_cstr(key), (uint8_t*)&f->access_rights, 2)) | ||||||
|  |                 break; | ||||||
|  |             uint16_t size = 0; | ||||||
|  |             if(f->type == MifareDesfireFileTypeStandard || | ||||||
|  |                f->type == MifareDesfireFileTypeBackup) { | ||||||
|  |                 size = f->settings.data.size; | ||||||
|  |                 string_printf(key, "%s File %d Size", string_get_cstr(prefix), f->id); | ||||||
|  |                 if(!flipper_format_write_uint32( | ||||||
|  |                        file, string_get_cstr(key), &f->settings.data.size, 1)) | ||||||
|  |                     break; | ||||||
|  |             } else if(f->type == MifareDesfireFileTypeValue) { | ||||||
|  |                 string_printf(key, "%s File %d Hi Limit", string_get_cstr(prefix), f->id); | ||||||
|  |                 if(!flipper_format_write_uint32( | ||||||
|  |                        file, string_get_cstr(key), &f->settings.value.hi_limit, 1)) | ||||||
|  |                     break; | ||||||
|  |                 string_printf(key, "%s File %d Lo Limit", string_get_cstr(prefix), f->id); | ||||||
|  |                 if(!flipper_format_write_uint32( | ||||||
|  |                        file, string_get_cstr(key), &f->settings.value.lo_limit, 1)) | ||||||
|  |                     break; | ||||||
|  |                 string_printf( | ||||||
|  |                     key, "%s File %d Limited Credit Value", string_get_cstr(prefix), f->id); | ||||||
|  |                 if(!flipper_format_write_uint32( | ||||||
|  |                        file, string_get_cstr(key), &f->settings.value.limited_credit_value, 1)) | ||||||
|  |                     break; | ||||||
|  |                 string_printf( | ||||||
|  |                     key, "%s File %d Limited Credit Enabled", string_get_cstr(prefix), f->id); | ||||||
|  |                 if(!flipper_format_write_bool( | ||||||
|  |                        file, string_get_cstr(key), &f->settings.value.limited_credit_enabled, 1)) | ||||||
|  |                     break; | ||||||
|  |                 size = 4; | ||||||
|  |             } else if( | ||||||
|  |                 f->type == MifareDesfireFileTypeLinearRecord || | ||||||
|  |                 f->type == MifareDesfireFileTypeCyclicRecord) { | ||||||
|  |                 string_printf(key, "%s File %d Size", string_get_cstr(prefix), f->id); | ||||||
|  |                 if(!flipper_format_write_uint32( | ||||||
|  |                        file, string_get_cstr(key), &f->settings.record.size, 1)) | ||||||
|  |                     break; | ||||||
|  |                 string_printf(key, "%s File %d Max", string_get_cstr(prefix), f->id); | ||||||
|  |                 if(!flipper_format_write_uint32( | ||||||
|  |                        file, string_get_cstr(key), &f->settings.record.max, 1)) | ||||||
|  |                     break; | ||||||
|  |                 string_printf(key, "%s File %d Cur", string_get_cstr(prefix), f->id); | ||||||
|  |                 if(!flipper_format_write_uint32( | ||||||
|  |                        file, string_get_cstr(key), &f->settings.record.cur, 1)) | ||||||
|  |                     break; | ||||||
|  |                 size = f->settings.record.size * f->settings.record.cur; | ||||||
|  |             } | ||||||
|  |             if(f->contents) { | ||||||
|  |                 string_printf(key, "%s File %d", string_get_cstr(prefix), f->id); | ||||||
|  |                 if(!flipper_format_write_hex(file, string_get_cstr(key), f->contents, size)) break; | ||||||
|  |             } | ||||||
|  |             saved_files = true; | ||||||
|  |         } | ||||||
|  |         if(!saved_files) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         saved = true; | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     free(tmp); | ||||||
|  |     string_clear(prefix); | ||||||
|  |     string_clear(key); | ||||||
|  |     return saved; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool nfc_device_load_mifare_df_app(FlipperFormat* file, MifareDesfireApplication* app) { | ||||||
|  |     bool parsed = false; | ||||||
|  |     string_t prefix, key; | ||||||
|  |     string_init_printf(prefix, "Application %02x%02x%02x", app->id[0], app->id[1], app->id[2]); | ||||||
|  |     string_init(key); | ||||||
|  |     uint8_t* tmp = NULL; | ||||||
|  |     MifareDesfireFile* f = NULL; | ||||||
|  | 
 | ||||||
|  |     do { | ||||||
|  |         app->key_settings = malloc(sizeof(MifareDesfireKeySettings)); | ||||||
|  |         memset(app->key_settings, 0, sizeof(MifareDesfireKeySettings)); | ||||||
|  |         if(!nfc_device_load_mifare_df_key_settings( | ||||||
|  |                file, app->key_settings, string_get_cstr(prefix))) { | ||||||
|  |             free(app->key_settings); | ||||||
|  |             app->key_settings = NULL; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         string_printf(key, "%s File IDs", string_get_cstr(prefix)); | ||||||
|  |         uint32_t n_files; | ||||||
|  |         if(!flipper_format_get_value_count(file, string_get_cstr(key), &n_files)) break; | ||||||
|  |         tmp = malloc(n_files); | ||||||
|  |         if(!flipper_format_read_hex(file, string_get_cstr(key), tmp, n_files)) break; | ||||||
|  |         MifareDesfireFile** file_head = &app->file_head; | ||||||
|  |         bool parsed_files = true; | ||||||
|  |         for(int i = 0; i < n_files; i++) { | ||||||
|  |             parsed_files = false; | ||||||
|  |             f = malloc(sizeof(MifareDesfireFile)); | ||||||
|  |             memset(f, 0, sizeof(MifareDesfireFile)); | ||||||
|  |             f->id = tmp[i]; | ||||||
|  |             string_printf(key, "%s File %d Type", string_get_cstr(prefix), f->id); | ||||||
|  |             if(!flipper_format_read_hex(file, string_get_cstr(key), &f->type, 1)) break; | ||||||
|  |             string_printf( | ||||||
|  |                 key, "%s File %d Communication Settings", string_get_cstr(prefix), f->id); | ||||||
|  |             if(!flipper_format_read_hex(file, string_get_cstr(key), &f->comm, 1)) break; | ||||||
|  |             string_printf(key, "%s File %d Access Rights", string_get_cstr(prefix), f->id); | ||||||
|  |             if(!flipper_format_read_hex(file, string_get_cstr(key), (uint8_t*)&f->access_rights, 2)) | ||||||
|  |                 break; | ||||||
|  |             if(f->type == MifareDesfireFileTypeStandard || | ||||||
|  |                f->type == MifareDesfireFileTypeBackup) { | ||||||
|  |                 string_printf(key, "%s File %d Size", string_get_cstr(prefix), f->id); | ||||||
|  |                 if(!flipper_format_read_uint32( | ||||||
|  |                        file, string_get_cstr(key), &f->settings.data.size, 1)) | ||||||
|  |                     break; | ||||||
|  |             } else if(f->type == MifareDesfireFileTypeValue) { | ||||||
|  |                 string_printf(key, "%s File %d Hi Limit", string_get_cstr(prefix), f->id); | ||||||
|  |                 if(!flipper_format_read_uint32( | ||||||
|  |                        file, string_get_cstr(key), &f->settings.value.hi_limit, 1)) | ||||||
|  |                     break; | ||||||
|  |                 string_printf(key, "%s File %d Lo Limit", string_get_cstr(prefix), f->id); | ||||||
|  |                 if(!flipper_format_read_uint32( | ||||||
|  |                        file, string_get_cstr(key), &f->settings.value.lo_limit, 1)) | ||||||
|  |                     break; | ||||||
|  |                 string_printf( | ||||||
|  |                     key, "%s File %d Limited Credit Value", string_get_cstr(prefix), f->id); | ||||||
|  |                 if(!flipper_format_read_uint32( | ||||||
|  |                        file, string_get_cstr(key), &f->settings.value.limited_credit_value, 1)) | ||||||
|  |                     break; | ||||||
|  |                 string_printf( | ||||||
|  |                     key, "%s File %d Limited Credit Enabled", string_get_cstr(prefix), f->id); | ||||||
|  |                 if(!flipper_format_read_bool( | ||||||
|  |                        file, string_get_cstr(key), &f->settings.value.limited_credit_enabled, 1)) | ||||||
|  |                     break; | ||||||
|  |             } else if( | ||||||
|  |                 f->type == MifareDesfireFileTypeLinearRecord || | ||||||
|  |                 f->type == MifareDesfireFileTypeCyclicRecord) { | ||||||
|  |                 string_printf(key, "%s File %d Size", string_get_cstr(prefix), f->id); | ||||||
|  |                 if(!flipper_format_read_uint32( | ||||||
|  |                        file, string_get_cstr(key), &f->settings.record.size, 1)) | ||||||
|  |                     break; | ||||||
|  |                 string_printf(key, "%s File %d Max", string_get_cstr(prefix), f->id); | ||||||
|  |                 if(!flipper_format_read_uint32( | ||||||
|  |                        file, string_get_cstr(key), &f->settings.record.max, 1)) | ||||||
|  |                     break; | ||||||
|  |                 string_printf(key, "%s File %d Cur", string_get_cstr(prefix), f->id); | ||||||
|  |                 if(!flipper_format_read_uint32( | ||||||
|  |                        file, string_get_cstr(key), &f->settings.record.cur, 1)) | ||||||
|  |                     break; | ||||||
|  |             } | ||||||
|  |             string_printf(key, "%s File %d", string_get_cstr(prefix), f->id); | ||||||
|  |             if(flipper_format_key_exist(file, string_get_cstr(key))) { | ||||||
|  |                 uint32_t size; | ||||||
|  |                 if(!flipper_format_get_value_count(file, string_get_cstr(key), &size)) break; | ||||||
|  |                 f->contents = malloc(size); | ||||||
|  |                 if(!flipper_format_read_hex(file, string_get_cstr(key), f->contents, size)) break; | ||||||
|  |             } | ||||||
|  |             *file_head = f; | ||||||
|  |             file_head = &f->next; | ||||||
|  |             f = NULL; | ||||||
|  |             parsed_files = true; | ||||||
|  |         } | ||||||
|  |         if(!parsed_files) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         parsed = true; | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     if(f) { | ||||||
|  |         free(f->contents); | ||||||
|  |         free(f); | ||||||
|  |     } | ||||||
|  |     free(tmp); | ||||||
|  |     string_clear(prefix); | ||||||
|  |     string_clear(key); | ||||||
|  |     return parsed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool nfc_device_save_mifare_df_data(FlipperFormat* file, NfcDevice* dev) { | ||||||
|  |     bool saved = false; | ||||||
|  |     MifareDesfireData* data = &dev->dev_data.mf_df_data; | ||||||
|  |     uint8_t* tmp = NULL; | ||||||
|  | 
 | ||||||
|  |     do { | ||||||
|  |         if(!flipper_format_write_comment_cstr(file, "Mifare DESFire specific data")) break; | ||||||
|  |         if(!flipper_format_write_hex( | ||||||
|  |                file, "PICC Version", (uint8_t*)&data->version, sizeof(data->version))) | ||||||
|  |             break; | ||||||
|  |         if(data->free_memory) { | ||||||
|  |             if(!flipper_format_write_uint32(file, "PICC Free Memory", &data->free_memory->bytes, 1)) | ||||||
|  |                 break; | ||||||
|  |         } | ||||||
|  |         if(data->master_key_settings) { | ||||||
|  |             if(!nfc_device_save_mifare_df_key_settings(file, data->master_key_settings, "PICC")) | ||||||
|  |                 break; | ||||||
|  |         } | ||||||
|  |         uint32_t n_apps = 0; | ||||||
|  |         for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { | ||||||
|  |             n_apps++; | ||||||
|  |         } | ||||||
|  |         if(!flipper_format_write_uint32(file, "Application Count", &n_apps, 1)) break; | ||||||
|  |         tmp = malloc(n_apps * 3); | ||||||
|  |         int i = 0; | ||||||
|  |         for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { | ||||||
|  |             memcpy(tmp + i, app->id, 3); | ||||||
|  |             i += 3; | ||||||
|  |         } | ||||||
|  |         if(!flipper_format_write_hex(file, "Application IDs", tmp, n_apps * 3)) break; | ||||||
|  |         for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { | ||||||
|  |             if(!nfc_device_save_mifare_df_app(file, app)) break; | ||||||
|  |         } | ||||||
|  |         saved = true; | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     free(tmp); | ||||||
|  |     return saved; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) { | ||||||
|  |     bool parsed = false; | ||||||
|  |     MifareDesfireData* data = &dev->dev_data.mf_df_data; | ||||||
|  |     memset(data, 0, sizeof(MifareDesfireData)); | ||||||
|  |     uint8_t* tmp = NULL; | ||||||
|  | 
 | ||||||
|  |     do { | ||||||
|  |         if(!flipper_format_read_hex( | ||||||
|  |                file, "PICC Version", (uint8_t*)&data->version, sizeof(data->version))) | ||||||
|  |             break; | ||||||
|  |         if(flipper_format_key_exist(file, "PICC Free Memory")) { | ||||||
|  |             data->free_memory = malloc(sizeof(MifareDesfireFreeMemory)); | ||||||
|  |             memset(data->free_memory, 0, sizeof(MifareDesfireFreeMemory)); | ||||||
|  |             if(!flipper_format_read_uint32( | ||||||
|  |                    file, "PICC Free Memory", &data->free_memory->bytes, 1)) { | ||||||
|  |                 free(data->free_memory); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         data->master_key_settings = malloc(sizeof(MifareDesfireKeySettings)); | ||||||
|  |         memset(data->master_key_settings, 0, sizeof(MifareDesfireKeySettings)); | ||||||
|  |         if(!nfc_device_load_mifare_df_key_settings(file, data->master_key_settings, "PICC")) { | ||||||
|  |             free(data->master_key_settings); | ||||||
|  |             data->master_key_settings = NULL; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         uint32_t n_apps; | ||||||
|  |         if(!flipper_format_read_uint32(file, "Application Count", &n_apps, 1)) break; | ||||||
|  |         tmp = malloc(n_apps * 3); | ||||||
|  |         if(!flipper_format_read_hex(file, "Application IDs", tmp, n_apps * 3)) break; | ||||||
|  |         bool parsed_apps = true; | ||||||
|  |         MifareDesfireApplication** app_head = &data->app_head; | ||||||
|  |         for(int i = 0; i < n_apps; i++) { | ||||||
|  |             MifareDesfireApplication* app = malloc(sizeof(MifareDesfireApplication)); | ||||||
|  |             memset(app, 0, sizeof(MifareDesfireApplication)); | ||||||
|  |             memcpy(app->id, &tmp[i * 3], 3); | ||||||
|  |             if(!nfc_device_load_mifare_df_app(file, app)) { | ||||||
|  |                 free(app); | ||||||
|  |                 parsed_apps = false; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             *app_head = app; | ||||||
|  |             app_head = &app->next; | ||||||
|  |         } | ||||||
|  |         if(!parsed_apps) break; | ||||||
|  |         parsed = true; | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     free(tmp); | ||||||
|  |     return parsed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static bool nfc_device_save_bank_card_data(FlipperFormat* file, NfcDevice* dev) { | static bool nfc_device_save_bank_card_data(FlipperFormat* file, NfcDevice* dev) { | ||||||
|     bool saved = false; |     bool saved = false; | ||||||
|     NfcEmvData* data = &dev->dev_data.emv_data; |     NfcEmvData* data = &dev->dev_data.emv_data; | ||||||
| @ -263,6 +648,8 @@ static bool nfc_device_save_file( | |||||||
|         // Save more data if necessary
 |         // Save more data if necessary
 | ||||||
|         if(dev->format == NfcDeviceSaveFormatMifareUl) { |         if(dev->format == NfcDeviceSaveFormatMifareUl) { | ||||||
|             if(!nfc_device_save_mifare_ul_data(file, dev)) break; |             if(!nfc_device_save_mifare_ul_data(file, dev)) break; | ||||||
|  |         } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { | ||||||
|  |             if(!nfc_device_save_mifare_df_data(file, dev)) break; | ||||||
|         } else if(dev->format == NfcDeviceSaveFormatBankCard) { |         } else if(dev->format == NfcDeviceSaveFormatBankCard) { | ||||||
|             if(!nfc_device_save_bank_card_data(file, dev)) break; |             if(!nfc_device_save_bank_card_data(file, dev)) break; | ||||||
|         } |         } | ||||||
| @ -327,6 +714,8 @@ static bool nfc_device_load_data(NfcDevice* dev, string_t path) { | |||||||
|         // Parse other data
 |         // Parse other data
 | ||||||
|         if(dev->format == NfcDeviceSaveFormatMifareUl) { |         if(dev->format == NfcDeviceSaveFormatMifareUl) { | ||||||
|             if(!nfc_device_load_mifare_ul_data(file, dev)) break; |             if(!nfc_device_load_mifare_ul_data(file, dev)) break; | ||||||
|  |         } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { | ||||||
|  |             if(!nfc_device_load_mifare_df_data(file, dev)) break; | ||||||
|         } else if(dev->format == NfcDeviceSaveFormatBankCard) { |         } else if(dev->format == NfcDeviceSaveFormatBankCard) { | ||||||
|             if(!nfc_device_load_bank_card_data(file, dev)) break; |             if(!nfc_device_load_bank_card_data(file, dev)) break; | ||||||
|         } |         } | ||||||
| @ -389,9 +778,16 @@ bool nfc_file_select(NfcDevice* dev) { | |||||||
|     return res; |     return res; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void nfc_device_data_clear(NfcDeviceData* dev_data) { | ||||||
|  |     if(dev_data->nfc_data.protocol == NfcDeviceProtocolMifareDesfire) { | ||||||
|  |         mf_df_clear(&dev_data->mf_df_data); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void nfc_device_clear(NfcDevice* dev) { | void nfc_device_clear(NfcDevice* dev) { | ||||||
|     furi_assert(dev); |     furi_assert(dev); | ||||||
| 
 | 
 | ||||||
|  |     nfc_device_data_clear(&dev->dev_data); | ||||||
|     memset(&dev->dev_data, 0, sizeof(dev->dev_data)); |     memset(&dev->dev_data, 0, sizeof(dev->dev_data)); | ||||||
|     dev->format = NfcDeviceSaveFormatUid; |     dev->format = NfcDeviceSaveFormatUid; | ||||||
| } | } | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ | |||||||
| #include <dialogs/dialogs.h> | #include <dialogs/dialogs.h> | ||||||
| 
 | 
 | ||||||
| #include "mifare_ultralight.h" | #include "mifare_ultralight.h" | ||||||
|  | #include "mifare_desfire.h" | ||||||
| 
 | 
 | ||||||
| #define NFC_DEV_NAME_MAX_LEN 22 | #define NFC_DEV_NAME_MAX_LEN 22 | ||||||
| #define NFC_FILE_NAME_MAX_LEN 120 | #define NFC_FILE_NAME_MAX_LEN 120 | ||||||
| @ -26,12 +27,14 @@ typedef enum { | |||||||
|     NfcDeviceProtocolUnknown, |     NfcDeviceProtocolUnknown, | ||||||
|     NfcDeviceProtocolEMV, |     NfcDeviceProtocolEMV, | ||||||
|     NfcDeviceProtocolMifareUl, |     NfcDeviceProtocolMifareUl, | ||||||
|  |     NfcDeviceProtocolMifareDesfire, | ||||||
| } NfcProtocol; | } NfcProtocol; | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     NfcDeviceSaveFormatUid, |     NfcDeviceSaveFormatUid, | ||||||
|     NfcDeviceSaveFormatBankCard, |     NfcDeviceSaveFormatBankCard, | ||||||
|     NfcDeviceSaveFormatMifareUl, |     NfcDeviceSaveFormatMifareUl, | ||||||
|  |     NfcDeviceSaveFormatMifareDesfire, | ||||||
| } NfcDeviceSaveFormat; | } NfcDeviceSaveFormat; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
| @ -62,10 +65,11 @@ typedef struct { | |||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     NfcDeviceCommonData nfc_data; |     NfcDeviceCommonData nfc_data; | ||||||
|  |     NfcReaderRequestData reader_data; | ||||||
|     union { |     union { | ||||||
|         NfcEmvData emv_data; |         NfcEmvData emv_data; | ||||||
|         MifareUlData mf_ul_data; |         MifareUlData mf_ul_data; | ||||||
|         NfcReaderRequestData reader_data; |         MifareDesfireData mf_df_data; | ||||||
|     }; |     }; | ||||||
| } NfcDeviceData; | } NfcDeviceData; | ||||||
| 
 | 
 | ||||||
| @ -93,6 +97,8 @@ bool nfc_device_load(NfcDevice* dev, const char* file_path); | |||||||
| 
 | 
 | ||||||
| bool nfc_file_select(NfcDevice* dev); | bool nfc_file_select(NfcDevice* dev); | ||||||
| 
 | 
 | ||||||
|  | void nfc_device_data_clear(NfcDeviceData* dev); | ||||||
|  | 
 | ||||||
| void nfc_device_clear(NfcDevice* dev); | void nfc_device_clear(NfcDevice* dev); | ||||||
| 
 | 
 | ||||||
| bool nfc_device_delete(NfcDevice* dev); | bool nfc_device_delete(NfcDevice* dev); | ||||||
|  | |||||||
| @ -53,6 +53,8 @@ const char* nfc_guess_protocol(NfcProtocol protocol) { | |||||||
|         return "EMV bank card"; |         return "EMV bank card"; | ||||||
|     } else if(protocol == NfcDeviceProtocolMifareUl) { |     } else if(protocol == NfcDeviceProtocolMifareUl) { | ||||||
|         return "Mifare Ultral/NTAG"; |         return "Mifare Ultral/NTAG"; | ||||||
|  |     } else if(protocol == NfcDeviceProtocolMifareDesfire) { | ||||||
|  |         return "Mifare DESFire"; | ||||||
|     } else { |     } else { | ||||||
|         return "Unrecognized"; |         return "Unrecognized"; | ||||||
|     } |     } | ||||||
|  | |||||||
							
								
								
									
										248
									
								
								applications/nfc/nfc_worker.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										248
									
								
								applications/nfc/nfc_worker.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -1,6 +1,7 @@ | |||||||
| #include "nfc_worker_i.h" | #include "nfc_worker_i.h" | ||||||
| #include <furi_hal.h> | #include <furi_hal.h> | ||||||
| #include "nfc_protocols/emv_decoder.h" | #include "nfc_protocols/emv_decoder.h" | ||||||
|  | #include "nfc_protocols/mifare_desfire.h" | ||||||
| #include "nfc_protocols/mifare_ultralight.h" | #include "nfc_protocols/mifare_ultralight.h" | ||||||
| 
 | 
 | ||||||
| #define TAG "NfcWorker" | #define TAG "NfcWorker" | ||||||
| @ -94,6 +95,8 @@ int32_t nfc_worker_task(void* context) { | |||||||
|         nfc_worker_read_mifare_ul(nfc_worker); |         nfc_worker_read_mifare_ul(nfc_worker); | ||||||
|     } else if(nfc_worker->state == NfcWorkerStateEmulateMifareUl) { |     } else if(nfc_worker->state == NfcWorkerStateEmulateMifareUl) { | ||||||
|         nfc_worker_emulate_mifare_ul(nfc_worker); |         nfc_worker_emulate_mifare_ul(nfc_worker); | ||||||
|  |     } else if(nfc_worker->state == NfcWorkerStateReadMifareDesfire) { | ||||||
|  |         nfc_worker_read_mifare_desfire(nfc_worker); | ||||||
|     } else if(nfc_worker->state == NfcWorkerStateField) { |     } else if(nfc_worker->state == NfcWorkerStateField) { | ||||||
|         nfc_worker_field(nfc_worker); |         nfc_worker_field(nfc_worker); | ||||||
|     } |     } | ||||||
| @ -108,6 +111,7 @@ void nfc_worker_detect(NfcWorker* nfc_worker) { | |||||||
|     rfalNfcDevice* dev_list; |     rfalNfcDevice* dev_list; | ||||||
|     rfalNfcDevice* dev; |     rfalNfcDevice* dev; | ||||||
|     uint8_t dev_cnt; |     uint8_t dev_cnt; | ||||||
|  |     nfc_device_data_clear(nfc_worker->dev_data); | ||||||
|     NfcDeviceCommonData* result = &nfc_worker->dev_data->nfc_data; |     NfcDeviceCommonData* result = &nfc_worker->dev_data->nfc_data; | ||||||
| 
 | 
 | ||||||
|     while(nfc_worker->state == NfcWorkerStateDetect) { |     while(nfc_worker->state == NfcWorkerStateDetect) { | ||||||
| @ -126,6 +130,11 @@ void nfc_worker_detect(NfcWorker* nfc_worker) { | |||||||
|                        dev->dev.nfca.sensRes.platformInfo, |                        dev->dev.nfca.sensRes.platformInfo, | ||||||
|                        dev->dev.nfca.selRes.sak)) { |                        dev->dev.nfca.selRes.sak)) { | ||||||
|                     result->protocol = NfcDeviceProtocolMifareUl; |                     result->protocol = NfcDeviceProtocolMifareUl; | ||||||
|  |                 } else if(mf_df_check_card_type( | ||||||
|  |                               dev->dev.nfca.sensRes.anticollisionInfo, | ||||||
|  |                               dev->dev.nfca.sensRes.platformInfo, | ||||||
|  |                               dev->dev.nfca.selRes.sak)) { | ||||||
|  |                     result->protocol = NfcDeviceProtocolMifareDesfire; | ||||||
|                 } else if(dev->rfInterface == RFAL_NFC_INTERFACE_ISODEP) { |                 } else if(dev->rfInterface == RFAL_NFC_INTERFACE_ISODEP) { | ||||||
|                     result->protocol = NfcDeviceProtocolEMV; |                     result->protocol = NfcDeviceProtocolEMV; | ||||||
|                 } else { |                 } else { | ||||||
| @ -192,6 +201,7 @@ void nfc_worker_read_emv_app(NfcWorker* nfc_worker) { | |||||||
|     uint8_t* rx_buff; |     uint8_t* rx_buff; | ||||||
|     uint16_t* rx_len; |     uint16_t* rx_len; | ||||||
|     NfcDeviceData* result = nfc_worker->dev_data; |     NfcDeviceData* result = nfc_worker->dev_data; | ||||||
|  |     nfc_device_data_clear(result); | ||||||
| 
 | 
 | ||||||
|     while(nfc_worker->state == NfcWorkerStateReadEMVApp) { |     while(nfc_worker->state == NfcWorkerStateReadEMVApp) { | ||||||
|         memset(&emv_app, 0, sizeof(emv_app)); |         memset(&emv_app, 0, sizeof(emv_app)); | ||||||
| @ -253,6 +263,7 @@ void nfc_worker_read_emv(NfcWorker* nfc_worker) { | |||||||
|     uint8_t* rx_buff; |     uint8_t* rx_buff; | ||||||
|     uint16_t* rx_len; |     uint16_t* rx_len; | ||||||
|     NfcDeviceData* result = nfc_worker->dev_data; |     NfcDeviceData* result = nfc_worker->dev_data; | ||||||
|  |     nfc_device_data_clear(result); | ||||||
| 
 | 
 | ||||||
|     while(nfc_worker->state == NfcWorkerStateReadEMV) { |     while(nfc_worker->state == NfcWorkerStateReadEMV) { | ||||||
|         memset(&emv_app, 0, sizeof(emv_app)); |         memset(&emv_app, 0, sizeof(emv_app)); | ||||||
| @ -516,6 +527,7 @@ void nfc_worker_read_mifare_ul(NfcWorker* nfc_worker) { | |||||||
|     uint16_t* rx_len; |     uint16_t* rx_len; | ||||||
|     MifareUlDevice mf_ul_read; |     MifareUlDevice mf_ul_read; | ||||||
|     NfcDeviceData* result = nfc_worker->dev_data; |     NfcDeviceData* result = nfc_worker->dev_data; | ||||||
|  |     nfc_device_data_clear(result); | ||||||
| 
 | 
 | ||||||
|     while(nfc_worker->state == NfcWorkerStateReadMifareUl) { |     while(nfc_worker->state == NfcWorkerStateReadMifareUl) { | ||||||
|         furi_hal_nfc_deactivate(); |         furi_hal_nfc_deactivate(); | ||||||
| @ -658,6 +670,242 @@ void nfc_worker_emulate_mifare_ul(NfcWorker* nfc_worker) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | ReturnCode nfc_exchange_full( | ||||||
|  |     uint8_t* tx_buff, | ||||||
|  |     uint16_t tx_len, | ||||||
|  |     uint8_t* rx_buff, | ||||||
|  |     uint16_t rx_cap, | ||||||
|  |     uint16_t* rx_len) { | ||||||
|  |     ReturnCode err; | ||||||
|  |     uint8_t* part_buff; | ||||||
|  |     uint16_t* part_len; | ||||||
|  | 
 | ||||||
|  |     err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &part_buff, &part_len, false); | ||||||
|  |     if(*part_len > rx_cap) { | ||||||
|  |         return ERR_OVERRUN; | ||||||
|  |     } | ||||||
|  |     memcpy(rx_buff, part_buff, *part_len); | ||||||
|  |     *rx_len = *part_len; | ||||||
|  |     while(err == ERR_NONE && rx_buff[0] == 0xAF) { | ||||||
|  |         err = furi_hal_nfc_data_exchange(rx_buff, 1, &part_buff, &part_len, false); | ||||||
|  |         if(*part_len > rx_cap - *rx_len) { | ||||||
|  |             return ERR_OVERRUN; | ||||||
|  |         } | ||||||
|  |         if(*part_len == 0) { | ||||||
|  |             return ERR_PROTO; | ||||||
|  |         } | ||||||
|  |         memcpy(rx_buff + *rx_len, part_buff + 1, *part_len - 1); | ||||||
|  |         *rx_buff = *part_buff; | ||||||
|  |         *rx_len += *part_len - 1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return err; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) { | ||||||
|  |     ReturnCode err; | ||||||
|  |     rfalNfcDevice* dev_list; | ||||||
|  |     uint8_t dev_cnt = 0; | ||||||
|  |     uint8_t tx_buff[64] = {}; | ||||||
|  |     uint16_t tx_len = 0; | ||||||
|  |     uint8_t rx_buff[512] = {}; | ||||||
|  |     uint16_t rx_len; | ||||||
|  |     NfcDeviceData* result = nfc_worker->dev_data; | ||||||
|  |     nfc_device_data_clear(result); | ||||||
|  |     MifareDesfireData* data = &result->mf_df_data; | ||||||
|  | 
 | ||||||
|  |     while(nfc_worker->state == NfcWorkerStateReadMifareDesfire) { | ||||||
|  |         furi_hal_nfc_deactivate(); | ||||||
|  |         if(!furi_hal_nfc_detect(&dev_list, &dev_cnt, 300, false)) { | ||||||
|  |             osDelay(100); | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         memset(data, 0, sizeof(MifareDesfireData)); | ||||||
|  |         if(dev_list[0].type != RFAL_NFC_LISTEN_TYPE_NFCA || | ||||||
|  |            !mf_df_check_card_type( | ||||||
|  |                dev_list[0].dev.nfca.sensRes.anticollisionInfo, | ||||||
|  |                dev_list[0].dev.nfca.sensRes.platformInfo, | ||||||
|  |                dev_list[0].dev.nfca.selRes.sak)) { | ||||||
|  |             FURI_LOG_D(TAG, "Tag is not DESFire"); | ||||||
|  |             osDelay(100); | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         FURI_LOG_D(TAG, "Found DESFire tag"); | ||||||
|  | 
 | ||||||
|  |         // Fill non-DESFire result data
 | ||||||
|  |         result->nfc_data.uid_len = dev_list[0].dev.nfca.nfcId1Len; | ||||||
|  |         result->nfc_data.atqa[0] = dev_list[0].dev.nfca.sensRes.anticollisionInfo; | ||||||
|  |         result->nfc_data.atqa[1] = dev_list[0].dev.nfca.sensRes.platformInfo; | ||||||
|  |         result->nfc_data.sak = dev_list[0].dev.nfca.selRes.sak; | ||||||
|  |         result->nfc_data.device = NfcDeviceNfca; | ||||||
|  |         result->nfc_data.protocol = NfcDeviceProtocolMifareDesfire; | ||||||
|  |         memcpy(result->nfc_data.uid, dev_list[0].dev.nfca.nfcId1, result->nfc_data.uid_len); | ||||||
|  | 
 | ||||||
|  |         // Get DESFire version
 | ||||||
|  |         tx_len = mf_df_prepare_get_version(tx_buff); | ||||||
|  |         err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||||
|  |         if(err != ERR_NONE) { | ||||||
|  |             FURI_LOG_W(TAG, "Bad exchange getting version, err: %d", err); | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         if(!mf_df_parse_get_version_response(rx_buff, rx_len, &data->version)) { | ||||||
|  |             FURI_LOG_W(TAG, "Bad DESFire GET_VERSION response"); | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         tx_len = mf_df_prepare_get_free_memory(tx_buff); | ||||||
|  |         err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||||
|  |         if(err == ERR_NONE) { | ||||||
|  |             data->free_memory = malloc(sizeof(MifareDesfireFreeMemory)); | ||||||
|  |             memset(data->free_memory, 0, sizeof(MifareDesfireFreeMemory)); | ||||||
|  |             if(!mf_df_parse_get_free_memory_response(rx_buff, rx_len, data->free_memory)) { | ||||||
|  |                 FURI_LOG_D(TAG, "Bad DESFire GET_FREE_MEMORY response (normal for pre-EV1 cards)"); | ||||||
|  |                 free(data->free_memory); | ||||||
|  |                 data->free_memory = NULL; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         tx_len = mf_df_prepare_get_key_settings(tx_buff); | ||||||
|  |         err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||||
|  |         if(err != ERR_NONE) { | ||||||
|  |             FURI_LOG_D(TAG, "Bad exchange getting key settings, err: %d", err); | ||||||
|  |         } else { | ||||||
|  |             data->master_key_settings = malloc(sizeof(MifareDesfireKeySettings)); | ||||||
|  |             memset(data->master_key_settings, 0, sizeof(MifareDesfireKeySettings)); | ||||||
|  |             if(!mf_df_parse_get_key_settings_response(rx_buff, rx_len, data->master_key_settings)) { | ||||||
|  |                 FURI_LOG_W(TAG, "Bad DESFire GET_KEY_SETTINGS response"); | ||||||
|  |                 free(data->master_key_settings); | ||||||
|  |                 data->master_key_settings = NULL; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             MifareDesfireKeyVersion** key_version_head = | ||||||
|  |                 &data->master_key_settings->key_version_head; | ||||||
|  |             for(uint8_t key_id = 0; key_id < data->master_key_settings->max_keys; key_id++) { | ||||||
|  |                 tx_len = mf_df_prepare_get_key_version(tx_buff, key_id); | ||||||
|  |                 err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||||
|  |                 if(err != ERR_NONE) { | ||||||
|  |                     FURI_LOG_W(TAG, "Bad exchange getting key version, err: %d", err); | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |                 MifareDesfireKeyVersion* key_version = malloc(sizeof(MifareDesfireKeyVersion)); | ||||||
|  |                 memset(key_version, 0, sizeof(MifareDesfireKeyVersion)); | ||||||
|  |                 key_version->id = key_id; | ||||||
|  |                 if(!mf_df_parse_get_key_version_response(rx_buff, rx_len, key_version)) { | ||||||
|  |                     FURI_LOG_W(TAG, "Bad DESFire GET_KEY_VERSION response"); | ||||||
|  |                     free(key_version); | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |                 *key_version_head = key_version; | ||||||
|  |                 key_version_head = &key_version->next; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         tx_len = mf_df_prepare_get_application_ids(tx_buff); | ||||||
|  |         err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||||
|  |         if(err != ERR_NONE) { | ||||||
|  |             FURI_LOG_W(TAG, "Bad exchange getting application IDs, err: %d", err); | ||||||
|  |         } else { | ||||||
|  |             if(!mf_df_parse_get_application_ids_response(rx_buff, rx_len, &data->app_head)) { | ||||||
|  |                 FURI_LOG_W(TAG, "Bad DESFire GET_APPLICATION_IDS response"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { | ||||||
|  |             tx_len = mf_df_prepare_select_application(tx_buff, app->id); | ||||||
|  |             err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||||
|  |             if(!mf_df_parse_select_application_response(rx_buff, rx_len)) { | ||||||
|  |                 FURI_LOG_W(TAG, "Bad exchange selecting application, err: %d", err); | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |             tx_len = mf_df_prepare_get_key_settings(tx_buff); | ||||||
|  |             err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||||
|  |             if(err != ERR_NONE) { | ||||||
|  |                 FURI_LOG_W(TAG, "Bad exchange getting key settings, err: %d", err); | ||||||
|  |             } else { | ||||||
|  |                 app->key_settings = malloc(sizeof(MifareDesfireKeySettings)); | ||||||
|  |                 memset(app->key_settings, 0, sizeof(MifareDesfireKeySettings)); | ||||||
|  |                 if(!mf_df_parse_get_key_settings_response(rx_buff, rx_len, app->key_settings)) { | ||||||
|  |                     FURI_LOG_W(TAG, "Bad DESFire GET_KEY_SETTINGS response"); | ||||||
|  |                     free(app->key_settings); | ||||||
|  |                     app->key_settings = NULL; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 MifareDesfireKeyVersion** key_version_head = &app->key_settings->key_version_head; | ||||||
|  |                 for(uint8_t key_id = 0; key_id < app->key_settings->max_keys; key_id++) { | ||||||
|  |                     tx_len = mf_df_prepare_get_key_version(tx_buff, key_id); | ||||||
|  |                     err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||||
|  |                     if(err != ERR_NONE) { | ||||||
|  |                         FURI_LOG_W(TAG, "Bad exchange getting key version, err: %d", err); | ||||||
|  |                         continue; | ||||||
|  |                     } | ||||||
|  |                     MifareDesfireKeyVersion* key_version = malloc(sizeof(MifareDesfireKeyVersion)); | ||||||
|  |                     memset(key_version, 0, sizeof(MifareDesfireKeyVersion)); | ||||||
|  |                     key_version->id = key_id; | ||||||
|  |                     if(!mf_df_parse_get_key_version_response(rx_buff, rx_len, key_version)) { | ||||||
|  |                         FURI_LOG_W(TAG, "Bad DESFire GET_KEY_VERSION response"); | ||||||
|  |                         free(key_version); | ||||||
|  |                         continue; | ||||||
|  |                     } | ||||||
|  |                     *key_version_head = key_version; | ||||||
|  |                     key_version_head = &key_version->next; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             tx_len = mf_df_prepare_get_file_ids(tx_buff); | ||||||
|  |             err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||||
|  |             if(err != ERR_NONE) { | ||||||
|  |                 FURI_LOG_W(TAG, "Bad exchange getting file IDs, err: %d", err); | ||||||
|  |             } else { | ||||||
|  |                 if(!mf_df_parse_get_file_ids_response(rx_buff, rx_len, &app->file_head)) { | ||||||
|  |                     FURI_LOG_W(TAG, "Bad DESFire GET_FILE_IDS response"); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             for(MifareDesfireFile* file = app->file_head; file; file = file->next) { | ||||||
|  |                 tx_len = mf_df_prepare_get_file_settings(tx_buff, file->id); | ||||||
|  |                 err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||||
|  |                 if(err != ERR_NONE) { | ||||||
|  |                     FURI_LOG_W(TAG, "Bad exchange getting file settings, err: %d", err); | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |                 if(!mf_df_parse_get_file_settings_response(rx_buff, rx_len, file)) { | ||||||
|  |                     FURI_LOG_W(TAG, "Bad DESFire GET_FILE_SETTINGS response"); | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |                 switch(file->type) { | ||||||
|  |                 case MifareDesfireFileTypeStandard: | ||||||
|  |                 case MifareDesfireFileTypeBackup: | ||||||
|  |                     tx_len = mf_df_prepare_read_data(tx_buff, file->id, 0, 0); | ||||||
|  |                     break; | ||||||
|  |                 case MifareDesfireFileTypeValue: | ||||||
|  |                     tx_len = mf_df_prepare_get_value(tx_buff, file->id); | ||||||
|  |                     break; | ||||||
|  |                 case MifareDesfireFileTypeLinearRecord: | ||||||
|  |                 case MifareDesfireFileTypeCyclicRecord: | ||||||
|  |                     tx_len = mf_df_prepare_read_records(tx_buff, file->id, 0, 0); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||||
|  |                 if(err != ERR_NONE) { | ||||||
|  |                     FURI_LOG_W(TAG, "Bad exchange reading file %d, err: %d", file->id, err); | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |                 if(!mf_df_parse_read_data_response(rx_buff, rx_len, file)) { | ||||||
|  |                     FURI_LOG_W(TAG, "Bad response reading file %d", file->id); | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Notify caller and exit
 | ||||||
|  |         if(nfc_worker->callback) { | ||||||
|  |             nfc_worker->callback(nfc_worker->context); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void nfc_worker_field(NfcWorker* nfc_worker) { | void nfc_worker_field(NfcWorker* nfc_worker) { | ||||||
|     furi_hal_nfc_field_on(); |     furi_hal_nfc_field_on(); | ||||||
|     while(nfc_worker->state == NfcWorkerStateField) { |     while(nfc_worker->state == NfcWorkerStateField) { | ||||||
|  | |||||||
| @ -18,6 +18,7 @@ typedef enum { | |||||||
|     NfcWorkerStateField, |     NfcWorkerStateField, | ||||||
|     NfcWorkerStateReadMifareUl, |     NfcWorkerStateReadMifareUl, | ||||||
|     NfcWorkerStateEmulateMifareUl, |     NfcWorkerStateEmulateMifareUl, | ||||||
|  |     NfcWorkerStateReadMifareDesfire, | ||||||
|     // Transition
 |     // Transition
 | ||||||
|     NfcWorkerStateStop, |     NfcWorkerStateStop, | ||||||
| } NfcWorkerState; | } NfcWorkerState; | ||||||
|  | |||||||
| @ -45,4 +45,6 @@ void nfc_worker_field(NfcWorker* nfc_worker); | |||||||
| 
 | 
 | ||||||
| void nfc_worker_read_mifare_ul(NfcWorker* nfc_worker); | void nfc_worker_read_mifare_ul(NfcWorker* nfc_worker); | ||||||
| 
 | 
 | ||||||
|  | void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker); | ||||||
|  | 
 | ||||||
| void nfc_worker_emulate_mifare_ul(NfcWorker* nfc_worker); | void nfc_worker_emulate_mifare_ul(NfcWorker* nfc_worker); | ||||||
|  | |||||||
| @ -50,6 +50,8 @@ bool nfc_scene_card_menu_on_event(void* context, SceneManagerEvent event) { | |||||||
|                 nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexRunApp); |                 nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexRunApp); | ||||||
|             if(nfc->dev->dev_data.nfc_data.protocol == NfcDeviceProtocolMifareUl) { |             if(nfc->dev->dev_data.nfc_data.protocol == NfcDeviceProtocolMifareUl) { | ||||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareUl); |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareUl); | ||||||
|  |             } else if(nfc->dev->dev_data.nfc_data.protocol == NfcDeviceProtocolMifareDesfire) { | ||||||
|  |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareDesfire); | ||||||
|             } else if(nfc->dev->dev_data.nfc_data.protocol == NfcDeviceProtocolEMV) { |             } else if(nfc->dev->dev_data.nfc_data.protocol == NfcDeviceProtocolEMV) { | ||||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvApp); |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvApp); | ||||||
|             } |             } | ||||||
|  | |||||||
| @ -19,6 +19,11 @@ ADD_SCENE(nfc, mifare_ul_menu, MifareUlMenu) | |||||||
| ADD_SCENE(nfc, emulate_mifare_ul, EmulateMifareUl) | ADD_SCENE(nfc, emulate_mifare_ul, EmulateMifareUl) | ||||||
| ADD_SCENE(nfc, read_emv_app, ReadEmvApp) | ADD_SCENE(nfc, read_emv_app, ReadEmvApp) | ||||||
| ADD_SCENE(nfc, read_emv_app_success, ReadEmvAppSuccess) | ADD_SCENE(nfc, read_emv_app_success, ReadEmvAppSuccess) | ||||||
|  | ADD_SCENE(nfc, read_mifare_desfire, ReadMifareDesfire) | ||||||
|  | ADD_SCENE(nfc, read_mifare_desfire_success, ReadMifareDesfireSuccess) | ||||||
|  | ADD_SCENE(nfc, mifare_desfire_menu, MifareDesfireMenu) | ||||||
|  | ADD_SCENE(nfc, mifare_desfire_data, MifareDesfireData) | ||||||
|  | ADD_SCENE(nfc, mifare_desfire_app, MifareDesfireApp) | ||||||
| ADD_SCENE(nfc, device_info, DeviceInfo) | ADD_SCENE(nfc, device_info, DeviceInfo) | ||||||
| ADD_SCENE(nfc, delete, Delete) | ADD_SCENE(nfc, delete, Delete) | ||||||
| ADD_SCENE(nfc, delete_success, DeleteSuccess) | ADD_SCENE(nfc, delete_success, DeleteSuccess) | ||||||
|  | |||||||
							
								
								
									
										27
									
								
								applications/nfc/scenes/nfc_scene_device_info.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										27
									
								
								applications/nfc/scenes/nfc_scene_device_info.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -32,7 +32,7 @@ void nfc_scene_device_info_on_enter(void* context) { | |||||||
| 
 | 
 | ||||||
|     // Setup Custom Widget view
 |     // Setup Custom Widget view
 | ||||||
|     widget_add_text_box_element( |     widget_add_text_box_element( | ||||||
|         nfc->widget, 0, 0, 128, 22, AlignCenter, AlignCenter, nfc->dev->dev_name); |         nfc->widget, 0, 0, 128, 22, AlignCenter, AlignTop, nfc->dev->dev_name); | ||||||
|     widget_add_button_element( |     widget_add_button_element( | ||||||
|         nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_device_info_widget_callback, nfc); |         nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_device_info_widget_callback, nfc); | ||||||
|     widget_add_button_element( |     widget_add_button_element( | ||||||
| @ -64,7 +64,8 @@ void nfc_scene_device_info_on_enter(void* context) { | |||||||
|     widget_add_string_element(nfc->widget, 64, 21, AlignCenter, AlignTop, FontSecondary, uid_str); |     widget_add_string_element(nfc->widget, 64, 21, AlignCenter, AlignTop, FontSecondary, uid_str); | ||||||
| 
 | 
 | ||||||
|     const char* protocol_name = NULL; |     const char* protocol_name = NULL; | ||||||
|     if(data->protocol == NfcDeviceProtocolEMV) { |     if(data->protocol == NfcDeviceProtocolEMV || | ||||||
|  |        data->protocol == NfcDeviceProtocolMifareDesfire) { | ||||||
|         protocol_name = nfc_guess_protocol(data->protocol); |         protocol_name = nfc_guess_protocol(data->protocol); | ||||||
|     } else if(data->protocol == NfcDeviceProtocolMifareUl) { |     } else if(data->protocol == NfcDeviceProtocolMifareUl) { | ||||||
|         protocol_name = nfc_mf_ul_type(nfc->dev->dev_data.mf_ul_data.type, false); |         protocol_name = nfc_mf_ul_type(nfc->dev->dev_data.mf_ul_data.type, false); | ||||||
| @ -101,6 +102,25 @@ void nfc_scene_device_info_on_enter(void* context) { | |||||||
|                 nfc->text_box_store, "%02X%02X ", mf_ul_data->data[i], mf_ul_data->data[i + 1]); |                 nfc->text_box_store, "%02X%02X ", mf_ul_data->data[i], mf_ul_data->data[i + 1]); | ||||||
|         } |         } | ||||||
|         text_box_set_text(text_box, string_get_cstr(nfc->text_box_store)); |         text_box_set_text(text_box, string_get_cstr(nfc->text_box_store)); | ||||||
|  |     } else if(nfc->dev->format == NfcDeviceSaveFormatMifareDesfire) { | ||||||
|  |         MifareDesfireData* mf_df_data = &nfc->dev->dev_data.mf_df_data; | ||||||
|  |         uint16_t n_apps = 0; | ||||||
|  |         uint16_t n_files = 0; | ||||||
|  |         for(MifareDesfireApplication* app = mf_df_data->app_head; app; app = app->next) { | ||||||
|  |             n_apps++; | ||||||
|  |             for(MifareDesfireFile* file = app->file_head; file; file = file->next) { | ||||||
|  |                 n_files++; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         nfc_text_store_set( | ||||||
|  |             nfc, | ||||||
|  |             "%d application%s, %d file%s", | ||||||
|  |             n_apps, | ||||||
|  |             n_apps == 1 ? "" : "s", | ||||||
|  |             n_files, | ||||||
|  |             n_files == 1 ? "" : "s"); | ||||||
|  |         widget_add_string_element( | ||||||
|  |             nfc->widget, 64, 17, AlignCenter, AlignBottom, FontSecondary, nfc->text_store); | ||||||
|     } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) { |     } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) { | ||||||
|         NfcEmvData* emv_data = &nfc->dev->dev_data.emv_data; |         NfcEmvData* emv_data = &nfc->dev->dev_data.emv_data; | ||||||
|         BankCard* bank_card = nfc->bank_card; |         BankCard* bank_card = nfc->bank_card; | ||||||
| @ -162,6 +182,9 @@ bool nfc_scene_device_info_on_event(void* context, SceneManagerEvent event) { | |||||||
|                     nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoData); |                     nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoData); | ||||||
|                 view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewBankCard); |                 view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewBankCard); | ||||||
|                 consumed = true; |                 consumed = true; | ||||||
|  |             } else if(nfc->dev->format == NfcDeviceSaveFormatMifareDesfire) { | ||||||
|  |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMifareDesfireData); | ||||||
|  |                 consumed = true; | ||||||
|             } |             } | ||||||
|         } else if(state == NfcSceneDeviceInfoData && event.event == NfcCustomEventViewExit) { |         } else if(state == NfcSceneDeviceInfoData && event.event == NfcCustomEventViewExit) { | ||||||
|             scene_manager_set_scene_state( |             scene_manager_set_scene_state( | ||||||
|  | |||||||
							
								
								
									
										119
									
								
								applications/nfc/scenes/nfc_scene_mifare_desfire_app.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								applications/nfc/scenes/nfc_scene_mifare_desfire_app.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,119 @@ | |||||||
|  | #include "../nfc_i.h" | ||||||
|  | 
 | ||||||
|  | #define TAG "NfcSceneMifareDesfireApp" | ||||||
|  | 
 | ||||||
|  | enum SubmenuIndex { | ||||||
|  |     SubmenuIndexAppInfo, | ||||||
|  |     SubmenuIndexDynamic, // dynamic indexes start here
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | MifareDesfireApplication* nfc_scene_mifare_desfire_app_get_app(Nfc* nfc) { | ||||||
|  |     uint32_t app_idx = | ||||||
|  |         scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireApp) >> 1; | ||||||
|  |     MifareDesfireApplication* app = nfc->dev->dev_data.mf_df_data.app_head; | ||||||
|  |     for(int i = 0; i < app_idx && app; i++) { | ||||||
|  |         app = app->next; | ||||||
|  |     } | ||||||
|  |     return app; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_mifare_desfire_app_submenu_callback(void* context, uint32_t index) { | ||||||
|  |     Nfc* nfc = (Nfc*)context; | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_mifare_desfire_app_on_enter(void* context) { | ||||||
|  |     Nfc* nfc = (Nfc*)context; | ||||||
|  |     Submenu* submenu = nfc->submenu; | ||||||
|  |     MifareDesfireApplication* app = nfc_scene_mifare_desfire_app_get_app(nfc); | ||||||
|  |     if(!app) { | ||||||
|  |         popup_set_icon(nfc->popup, 5, 5, &I_WarningDolphin_45x42); | ||||||
|  |         popup_set_header(nfc->popup, "Internal Error!", 55, 12, AlignLeft, AlignBottom); | ||||||
|  |         popup_set_text( | ||||||
|  |             nfc->popup, | ||||||
|  |             "No app selected.\nThis should\nnever happen,\nplease file a bug.", | ||||||
|  |             55, | ||||||
|  |             15, | ||||||
|  |             AlignLeft, | ||||||
|  |             AlignTop); | ||||||
|  |         view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); | ||||||
|  |         FURI_LOG_E(TAG, "Bad state. No app selected?"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     text_box_set_font(nfc->text_box, TextBoxFontHex); | ||||||
|  | 
 | ||||||
|  |     submenu_add_item( | ||||||
|  |         submenu, | ||||||
|  |         "App info", | ||||||
|  |         SubmenuIndexAppInfo, | ||||||
|  |         nfc_scene_mifare_desfire_app_submenu_callback, | ||||||
|  |         nfc); | ||||||
|  | 
 | ||||||
|  |     uint16_t cap = NFC_TEXT_STORE_SIZE; | ||||||
|  |     char* buf = nfc->text_store; | ||||||
|  |     int idx = SubmenuIndexDynamic; | ||||||
|  |     for(MifareDesfireFile* file = app->file_head; file; file = file->next) { | ||||||
|  |         int size = snprintf(buf, cap, "File %d", file->id); | ||||||
|  |         if(size < 0 || size >= cap) { | ||||||
|  |             FURI_LOG_W( | ||||||
|  |                 TAG, | ||||||
|  |                 "Exceeded NFC_TEXT_STORE_SIZE when preparing file id strings; menu truncated"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         char* label = buf; | ||||||
|  |         cap -= size + 1; | ||||||
|  |         buf += size + 1; | ||||||
|  |         submenu_add_item( | ||||||
|  |             submenu, label, idx++, nfc_scene_mifare_desfire_app_submenu_callback, nfc); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool nfc_scene_mifare_desfire_app_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     Nfc* nfc = (Nfc*)context; | ||||||
|  |     uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireApp); | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         MifareDesfireApplication* app = nfc_scene_mifare_desfire_app_get_app(nfc); | ||||||
|  |         TextBox* text_box = nfc->text_box; | ||||||
|  |         string_reset(nfc->text_box_store); | ||||||
|  |         if(event.event == SubmenuIndexAppInfo) { | ||||||
|  |             mf_df_cat_application_info(app, nfc->text_box_store); | ||||||
|  |         } else { | ||||||
|  |             uint16_t index = event.event - SubmenuIndexDynamic; | ||||||
|  |             MifareDesfireFile* file = app->file_head; | ||||||
|  |             for(int i = 0; file && i < index; i++) { | ||||||
|  |                 file = file->next; | ||||||
|  |             } | ||||||
|  |             if(!file) { | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |             mf_df_cat_file(file, nfc->text_box_store); | ||||||
|  |         } | ||||||
|  |         text_box_set_text(text_box, string_get_cstr(nfc->text_box_store)); | ||||||
|  |         scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMifareDesfireApp, state | 1); | ||||||
|  |         view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); | ||||||
|  |         return true; | ||||||
|  |     } else if(event.type == SceneManagerEventTypeBack) { | ||||||
|  |         if(state & 1) { | ||||||
|  |             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); | ||||||
|  |             scene_manager_set_scene_state( | ||||||
|  |                 nfc->scene_manager, NfcSceneMifareDesfireApp, state & ~1); | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_mifare_desfire_app_on_exit(void* context) { | ||||||
|  |     Nfc* nfc = (Nfc*)context; | ||||||
|  | 
 | ||||||
|  |     text_box_reset(nfc->text_box); | ||||||
|  |     string_reset(nfc->text_box_store); | ||||||
|  | 
 | ||||||
|  |     submenu_reset(nfc->submenu); | ||||||
|  | } | ||||||
							
								
								
									
										108
									
								
								applications/nfc/scenes/nfc_scene_mifare_desfire_data.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								applications/nfc/scenes/nfc_scene_mifare_desfire_data.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,108 @@ | |||||||
|  | #include "../nfc_i.h" | ||||||
|  | 
 | ||||||
|  | #define TAG "NfcSceneMifareDesfireData" | ||||||
|  | 
 | ||||||
|  | enum { | ||||||
|  |     MifareDesfireDataStateMenu, | ||||||
|  |     MifareDesfireDataStateItem, // MUST be last, states >= this correspond with submenu index
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum SubmenuIndex { | ||||||
|  |     SubmenuIndexCardInfo, | ||||||
|  |     SubmenuIndexDynamic, // dynamic indexes start here
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void nfc_scene_mifare_desfire_data_submenu_callback(void* context, uint32_t index) { | ||||||
|  |     Nfc* nfc = (Nfc*)context; | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_mifare_desfire_data_on_enter(void* context) { | ||||||
|  |     Nfc* nfc = (Nfc*)context; | ||||||
|  |     Submenu* submenu = nfc->submenu; | ||||||
|  |     uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireData); | ||||||
|  |     MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data; | ||||||
|  | 
 | ||||||
|  |     text_box_set_font(nfc->text_box, TextBoxFontHex); | ||||||
|  | 
 | ||||||
|  |     submenu_add_item( | ||||||
|  |         submenu, | ||||||
|  |         "Card info", | ||||||
|  |         SubmenuIndexCardInfo, | ||||||
|  |         nfc_scene_mifare_desfire_data_submenu_callback, | ||||||
|  |         nfc); | ||||||
|  | 
 | ||||||
|  |     uint16_t cap = NFC_TEXT_STORE_SIZE; | ||||||
|  |     char* buf = nfc->text_store; | ||||||
|  |     int idx = SubmenuIndexDynamic; | ||||||
|  |     for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { | ||||||
|  |         int size = snprintf(buf, cap, "App %02x%02x%02x", app->id[0], app->id[1], app->id[2]); | ||||||
|  |         if(size < 0 || size >= cap) { | ||||||
|  |             FURI_LOG_W( | ||||||
|  |                 TAG, "Exceeded NFC_TEXT_STORE_SIZE when preparing app id strings; menu truncated"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         char* label = buf; | ||||||
|  |         cap -= size + 1; | ||||||
|  |         buf += size + 1; | ||||||
|  |         submenu_add_item( | ||||||
|  |             submenu, label, idx++, nfc_scene_mifare_desfire_data_submenu_callback, nfc); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(state >= MifareDesfireDataStateItem) { | ||||||
|  |         submenu_set_selected_item( | ||||||
|  |             nfc->submenu, state - MifareDesfireDataStateItem + SubmenuIndexDynamic); | ||||||
|  |         scene_manager_set_scene_state( | ||||||
|  |             nfc->scene_manager, NfcSceneMifareDesfireData, MifareDesfireDataStateMenu); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool nfc_scene_mifare_desfire_data_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     Nfc* nfc = (Nfc*)context; | ||||||
|  |     uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireData); | ||||||
|  |     MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         TextBox* text_box = nfc->text_box; | ||||||
|  |         string_reset(nfc->text_box_store); | ||||||
|  |         if(event.event == SubmenuIndexCardInfo) { | ||||||
|  |             mf_df_cat_card_info(data, nfc->text_box_store); | ||||||
|  |             text_box_set_text(text_box, string_get_cstr(nfc->text_box_store)); | ||||||
|  |             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); | ||||||
|  |             scene_manager_set_scene_state( | ||||||
|  |                 nfc->scene_manager, | ||||||
|  |                 NfcSceneMifareDesfireData, | ||||||
|  |                 MifareDesfireDataStateItem + SubmenuIndexCardInfo); | ||||||
|  |             return true; | ||||||
|  |         } else { | ||||||
|  |             uint16_t index = event.event - SubmenuIndexDynamic; | ||||||
|  |             scene_manager_set_scene_state( | ||||||
|  |                 nfc->scene_manager, NfcSceneMifareDesfireData, MifareDesfireDataStateItem + index); | ||||||
|  |             scene_manager_set_scene_state( | ||||||
|  |                 nfc->scene_manager, NfcSceneMifareDesfireApp, index << 1); | ||||||
|  |             scene_manager_next_scene(nfc->scene_manager, NfcSceneMifareDesfireApp); | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |     } else if(event.type == SceneManagerEventTypeBack) { | ||||||
|  |         if(state >= MifareDesfireDataStateItem) { | ||||||
|  |             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); | ||||||
|  |             scene_manager_set_scene_state( | ||||||
|  |                 nfc->scene_manager, NfcSceneMifareDesfireData, MifareDesfireDataStateMenu); | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_mifare_desfire_data_on_exit(void* context) { | ||||||
|  |     Nfc* nfc = (Nfc*)context; | ||||||
|  | 
 | ||||||
|  |     text_box_reset(nfc->text_box); | ||||||
|  |     string_reset(nfc->text_box_store); | ||||||
|  | 
 | ||||||
|  |     submenu_reset(nfc->submenu); | ||||||
|  | } | ||||||
							
								
								
									
										52
									
								
								applications/nfc/scenes/nfc_scene_mifare_desfire_menu.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								applications/nfc/scenes/nfc_scene_mifare_desfire_menu.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | |||||||
|  | #include "../nfc_i.h" | ||||||
|  | 
 | ||||||
|  | enum SubmenuIndex { | ||||||
|  |     SubmenuIndexSave, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void nfc_scene_mifare_desfire_menu_submenu_callback(void* context, uint32_t index) { | ||||||
|  |     Nfc* nfc = (Nfc*)context; | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_mifare_desfire_menu_on_enter(void* context) { | ||||||
|  |     Nfc* nfc = (Nfc*)context; | ||||||
|  |     Submenu* submenu = nfc->submenu; | ||||||
|  | 
 | ||||||
|  |     submenu_add_item( | ||||||
|  |         submenu, | ||||||
|  |         "Name and save", | ||||||
|  |         SubmenuIndexSave, | ||||||
|  |         nfc_scene_mifare_desfire_menu_submenu_callback, | ||||||
|  |         nfc); | ||||||
|  |     submenu_set_selected_item( | ||||||
|  |         nfc->submenu, | ||||||
|  |         scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireMenu)); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool nfc_scene_mifare_desfire_menu_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     Nfc* nfc = (Nfc*)context; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == SubmenuIndexSave) { | ||||||
|  |             scene_manager_set_scene_state( | ||||||
|  |                 nfc->scene_manager, NfcSceneMifareDesfireMenu, SubmenuIndexSave); | ||||||
|  |             nfc->dev->format = NfcDeviceSaveFormatMifareDesfire; | ||||||
|  |             // Clear device name
 | ||||||
|  |             nfc_device_set_name(nfc->dev, ""); | ||||||
|  |             scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_mifare_desfire_menu_on_exit(void* context) { | ||||||
|  |     Nfc* nfc = (Nfc*)context; | ||||||
|  | 
 | ||||||
|  |     submenu_reset(nfc->submenu); | ||||||
|  | } | ||||||
							
								
								
									
										56
									
								
								applications/nfc/scenes/nfc_scene_read_mifare_desfire.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								applications/nfc/scenes/nfc_scene_read_mifare_desfire.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,56 @@ | |||||||
|  | #include "../nfc_i.h" | ||||||
|  | #include <dolphin/dolphin.h> | ||||||
|  | 
 | ||||||
|  | void nfc_read_mifare_desfire_worker_callback(void* context) { | ||||||
|  |     Nfc* nfc = (Nfc*)context; | ||||||
|  |     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_read_mifare_desfire_on_enter(void* context) { | ||||||
|  |     Nfc* nfc = (Nfc*)context; | ||||||
|  |     DOLPHIN_DEED(DolphinDeedNfcRead); | ||||||
|  | 
 | ||||||
|  |     // Setup view
 | ||||||
|  |     Popup* popup = nfc->popup; | ||||||
|  |     popup_set_header(popup, "Reading\nDESFire", 70, 34, AlignLeft, AlignTop); | ||||||
|  |     popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); | ||||||
|  |     // Start worker
 | ||||||
|  |     nfc_worker_start( | ||||||
|  |         nfc->worker, | ||||||
|  |         NfcWorkerStateReadMifareDesfire, | ||||||
|  |         &nfc->dev->dev_data, | ||||||
|  |         nfc_read_mifare_desfire_worker_callback, | ||||||
|  |         nfc); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool nfc_scene_read_mifare_desfire_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     Nfc* nfc = (Nfc*)context; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == NfcCustomEventWorkerExit) { | ||||||
|  |             notification_message(nfc->notifications, &sequence_success); | ||||||
|  |             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareDesfireSuccess); | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |     } else if(event.type == SceneManagerEventTypeTick) { | ||||||
|  |         notification_message(nfc->notifications, &sequence_blink_blue_10); | ||||||
|  |         DOLPHIN_DEED(DolphinDeedNfcReadSuccess); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_read_mifare_desfire_on_exit(void* context) { | ||||||
|  |     Nfc* nfc = (Nfc*)context; | ||||||
|  | 
 | ||||||
|  |     // Stop worker
 | ||||||
|  |     nfc_worker_stop(nfc->worker); | ||||||
|  | 
 | ||||||
|  |     // Clear view
 | ||||||
|  |     Popup* popup = nfc->popup; | ||||||
|  |     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); | ||||||
|  |     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||||
|  |     popup_set_icon(popup, 0, 0, NULL); | ||||||
|  | } | ||||||
							
								
								
									
										106
									
								
								applications/nfc/scenes/nfc_scene_read_mifare_desfire_success.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								applications/nfc/scenes/nfc_scene_read_mifare_desfire_success.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,106 @@ | |||||||
|  | #include "../nfc_i.h" | ||||||
|  | #include <dolphin/dolphin.h> | ||||||
|  | 
 | ||||||
|  | #define NFC_SCENE_READ_SUCCESS_SHIFT "              " | ||||||
|  | 
 | ||||||
|  | enum { | ||||||
|  |     ReadMifareDesfireSuccessStateShowUID, | ||||||
|  |     ReadMifareDesfireSuccessStateShowData, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void nfc_scene_read_mifare_desfire_success_dialog_callback(DialogExResult result, void* context) { | ||||||
|  |     Nfc* nfc = (Nfc*)context; | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_send_custom_event(nfc->view_dispatcher, result); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_read_mifare_desfire_success_on_enter(void* context) { | ||||||
|  |     Nfc* nfc = (Nfc*)context; | ||||||
|  | 
 | ||||||
|  |     MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data; | ||||||
|  |     DialogEx* dialog_ex = nfc->dialog_ex; | ||||||
|  |     dialog_ex_set_left_button_text(dialog_ex, "Back"); | ||||||
|  |     dialog_ex_set_center_button_text(dialog_ex, "Data"); | ||||||
|  |     dialog_ex_set_right_button_text(dialog_ex, "More"); | ||||||
|  |     dialog_ex_set_icon(dialog_ex, 8, 16, &I_Medium_chip_22x21); | ||||||
|  | 
 | ||||||
|  |     uint16_t n_apps = 0; | ||||||
|  |     uint16_t n_files = 0; | ||||||
|  | 
 | ||||||
|  |     for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { | ||||||
|  |         n_apps++; | ||||||
|  |         for(MifareDesfireFile* file = app->file_head; file; file = file->next) { | ||||||
|  |             n_files++; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     nfc_text_store_set( | ||||||
|  |         nfc, | ||||||
|  |         "UID: %02X %02X %02X %02X %02X %02X %02X\n" NFC_SCENE_READ_SUCCESS_SHIFT | ||||||
|  |         "%d%s bytes\n" NFC_SCENE_READ_SUCCESS_SHIFT "%d bytes free\n" | ||||||
|  |         "%d application%s, %d file%s", | ||||||
|  |         data->version.uid[0], | ||||||
|  |         data->version.uid[1], | ||||||
|  |         data->version.uid[2], | ||||||
|  |         data->version.uid[3], | ||||||
|  |         data->version.uid[4], | ||||||
|  |         data->version.uid[5], | ||||||
|  |         data->version.uid[6], | ||||||
|  |         1 << (data->version.sw_storage >> 1), | ||||||
|  |         (data->version.sw_storage & 1) ? "+" : "", | ||||||
|  |         data->free_memory ? data->free_memory->bytes : 0, | ||||||
|  |         n_apps, | ||||||
|  |         n_apps == 1 ? "" : "s", | ||||||
|  |         n_files, | ||||||
|  |         n_files == 1 ? "" : "s"); | ||||||
|  |     dialog_ex_set_text(dialog_ex, nfc->text_store, 8, 6, AlignLeft, AlignTop); | ||||||
|  |     dialog_ex_set_context(dialog_ex, nfc); | ||||||
|  |     dialog_ex_set_result_callback( | ||||||
|  |         dialog_ex, nfc_scene_read_mifare_desfire_success_dialog_callback); | ||||||
|  | 
 | ||||||
|  |     scene_manager_set_scene_state( | ||||||
|  |         nfc->scene_manager, | ||||||
|  |         NfcSceneReadMifareDesfireSuccess, | ||||||
|  |         ReadMifareDesfireSuccessStateShowUID); | ||||||
|  |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool nfc_scene_read_mifare_desfire_success_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     uint32_t state = | ||||||
|  |         scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadMifareDesfireSuccess); | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(state == ReadMifareDesfireSuccessStateShowUID && event.event == DialogExResultLeft) { | ||||||
|  |             scene_manager_previous_scene(nfc->scene_manager); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if( | ||||||
|  |             state == ReadMifareDesfireSuccessStateShowUID && event.event == DialogExResultCenter) { | ||||||
|  |             scene_manager_next_scene(nfc->scene_manager, NfcSceneMifareDesfireData); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(state == ReadMifareDesfireSuccessStateShowUID && event.event == DialogExResultRight) { | ||||||
|  |             scene_manager_next_scene(nfc->scene_manager, NfcSceneMifareDesfireMenu); | ||||||
|  |             consumed = true; | ||||||
|  |         } | ||||||
|  |     } else if(event.type == SceneManagerEventTypeBack) { | ||||||
|  |         if(state == ReadMifareDesfireSuccessStateShowData) { | ||||||
|  |             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); | ||||||
|  |             scene_manager_set_scene_state( | ||||||
|  |                 nfc->scene_manager, | ||||||
|  |                 NfcSceneReadMifareDesfireSuccess, | ||||||
|  |                 ReadMifareDesfireSuccessStateShowUID); | ||||||
|  |             consumed = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_read_mifare_desfire_success_on_exit(void* context) { | ||||||
|  |     Nfc* nfc = (Nfc*)context; | ||||||
|  | 
 | ||||||
|  |     // Clean dialog
 | ||||||
|  |     DialogEx* dialog_ex = nfc->dialog_ex; | ||||||
|  |     dialog_ex_reset(dialog_ex); | ||||||
|  | } | ||||||
							
								
								
									
										4
									
								
								applications/nfc/scenes/nfc_scene_save_success.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										4
									
								
								applications/nfc/scenes/nfc_scene_save_success.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -33,6 +33,10 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { | |||||||
|             } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { |             } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { | ||||||
|                 consumed = scene_manager_search_and_switch_to_another_scene( |                 consumed = scene_manager_search_and_switch_to_another_scene( | ||||||
|                     nfc->scene_manager, NfcSceneFileSelect); |                     nfc->scene_manager, NfcSceneFileSelect); | ||||||
|  |             } else if(scene_manager_has_previous_scene( | ||||||
|  |                           nfc->scene_manager, NfcSceneMifareDesfireMenu)) { | ||||||
|  |                 consumed = scene_manager_search_and_switch_to_previous_scene( | ||||||
|  |                     nfc->scene_manager, NfcSceneMifareDesfireMenu); | ||||||
|             } else { |             } else { | ||||||
|                 consumed = scene_manager_search_and_switch_to_previous_scene( |                 consumed = scene_manager_search_and_switch_to_previous_scene( | ||||||
|                     nfc->scene_manager, NfcSceneStart); |                     nfc->scene_manager, NfcSceneStart); | ||||||
|  | |||||||
							
								
								
									
										17
									
								
								applications/nfc/scenes/nfc_scene_saved_menu.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										17
									
								
								applications/nfc/scenes/nfc_scene_saved_menu.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -18,9 +18,22 @@ void nfc_scene_saved_menu_on_enter(void* context) { | |||||||
|     Nfc* nfc = (Nfc*)context; |     Nfc* nfc = (Nfc*)context; | ||||||
|     Submenu* submenu = nfc->submenu; |     Submenu* submenu = nfc->submenu; | ||||||
| 
 | 
 | ||||||
|     if(nfc->dev->format != NfcDeviceSaveFormatBankCard) { |     if(nfc->dev->format == NfcDeviceSaveFormatUid || | ||||||
|  |        nfc->dev->format == NfcDeviceSaveFormatMifareDesfire || | ||||||
|  |        nfc->dev->format == NfcDeviceSaveFormatBankCard) { | ||||||
|         submenu_add_item( |         submenu_add_item( | ||||||
|             submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc); |             submenu, | ||||||
|  |             "Emulate UID", | ||||||
|  |             SubmenuIndexEmulate, | ||||||
|  |             nfc_scene_saved_menu_submenu_callback, | ||||||
|  |             nfc); | ||||||
|  |     } else if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { | ||||||
|  |         submenu_add_item( | ||||||
|  |             submenu, | ||||||
|  |             "Emulate Ultralight", | ||||||
|  |             SubmenuIndexEmulate, | ||||||
|  |             nfc_scene_saved_menu_submenu_callback, | ||||||
|  |             nfc); | ||||||
|     } |     } | ||||||
|     submenu_add_item( |     submenu_add_item( | ||||||
|         submenu, "Edit UID and name", SubmenuIndexEdit, nfc_scene_saved_menu_submenu_callback, nfc); |         submenu, "Edit UID and name", SubmenuIndexEdit, nfc_scene_saved_menu_submenu_callback, nfc); | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ | |||||||
| enum SubmenuIndex { | enum SubmenuIndex { | ||||||
|     SubmenuIndexBankCard, |     SubmenuIndexBankCard, | ||||||
|     SubmenuIndexMifareUltralight, |     SubmenuIndexMifareUltralight, | ||||||
|  |     SubmenuIndexMifareDesfire, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void nfc_scene_scripts_menu_submenu_callback(void* context, uint32_t index) { | void nfc_scene_scripts_menu_submenu_callback(void* context, uint32_t index) { | ||||||
| @ -27,6 +28,12 @@ void nfc_scene_scripts_menu_on_enter(void* context) { | |||||||
|         SubmenuIndexMifareUltralight, |         SubmenuIndexMifareUltralight, | ||||||
|         nfc_scene_scripts_menu_submenu_callback, |         nfc_scene_scripts_menu_submenu_callback, | ||||||
|         nfc); |         nfc); | ||||||
|  |     submenu_add_item( | ||||||
|  |         submenu, | ||||||
|  |         "Read Mifare DESFire", | ||||||
|  |         SubmenuIndexMifareDesfire, | ||||||
|  |         nfc_scene_scripts_menu_submenu_callback, | ||||||
|  |         nfc); | ||||||
|     submenu_set_selected_item( |     submenu_set_selected_item( | ||||||
|         nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneScriptsMenu)); |         nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneScriptsMenu)); | ||||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); | ||||||
| @ -46,6 +53,11 @@ bool nfc_scene_scripts_menu_on_event(void* context, SceneManagerEvent event) { | |||||||
|                 nfc->scene_manager, NfcSceneScriptsMenu, SubmenuIndexMifareUltralight); |                 nfc->scene_manager, NfcSceneScriptsMenu, SubmenuIndexMifareUltralight); | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareUl); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareUl); | ||||||
|             return true; |             return true; | ||||||
|  |         } else if(event.event == SubmenuIndexMifareDesfire) { | ||||||
|  |             scene_manager_set_scene_state( | ||||||
|  |                 nfc->scene_manager, NfcSceneScriptsMenu, SubmenuIndexMifareDesfire); | ||||||
|  |             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareDesfire); | ||||||
|  |             return true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -26,6 +26,10 @@ static const char* test_float_key = "Float data"; | |||||||
| static const float test_float_data[] = {1.5f, 1000.0f}; | static const float test_float_data[] = {1.5f, 1000.0f}; | ||||||
| static const float test_float_updated_data[] = {1.2f}; | static const float test_float_updated_data[] = {1.2f}; | ||||||
| 
 | 
 | ||||||
|  | static const char* test_bool_key = "Bool data"; | ||||||
|  | static const bool test_bool_data[] = {true, false}; | ||||||
|  | static const bool test_bool_updated_data[] = {false, true, true}; | ||||||
|  | 
 | ||||||
| static const char* test_hex_key = "Hex data"; | static const char* test_hex_key = "Hex data"; | ||||||
| static const uint8_t test_hex_data[] = {0xDE, 0xAD, 0xBE}; | static const uint8_t test_hex_data[] = {0xDE, 0xAD, 0xBE}; | ||||||
| static const uint8_t test_hex_updated_data[] = {0xFE, 0xCA}; | static const uint8_t test_hex_updated_data[] = {0xFE, 0xCA}; | ||||||
| @ -38,6 +42,7 @@ static const char* test_data_nix = "Filetype: Flipper File test\n" | |||||||
|                                    "Int32 data: 1234 -6345 7813 0\n" |                                    "Int32 data: 1234 -6345 7813 0\n" | ||||||
|                                    "Uint32 data: 1234 0 5678 9098 7654321\n" |                                    "Uint32 data: 1234 0 5678 9098 7654321\n" | ||||||
|                                    "Float data: 1.5 1000.0\n" |                                    "Float data: 1.5 1000.0\n" | ||||||
|  |                                    "Bool data: true false\n" | ||||||
|                                    "Hex data: DE AD BE"; |                                    "Hex data: DE AD BE"; | ||||||
| 
 | 
 | ||||||
| #define READ_TEST_WIN "ff_win.test" | #define READ_TEST_WIN "ff_win.test" | ||||||
| @ -48,6 +53,7 @@ static const char* test_data_win = "Filetype: Flipper File test\r\n" | |||||||
|                                    "Int32 data: 1234 -6345 7813 0\r\n" |                                    "Int32 data: 1234 -6345 7813 0\r\n" | ||||||
|                                    "Uint32 data: 1234 0 5678 9098 7654321\r\n" |                                    "Uint32 data: 1234 0 5678 9098 7654321\r\n" | ||||||
|                                    "Float data: 1.5 1000.0\r\n" |                                    "Float data: 1.5 1000.0\r\n" | ||||||
|  |                                    "Bool data: true false\r\n" | ||||||
|                                    "Hex data: DE AD BE"; |                                    "Hex data: DE AD BE"; | ||||||
| 
 | 
 | ||||||
| #define READ_TEST_FLP "ff_flp.test" | #define READ_TEST_FLP "ff_flp.test" | ||||||
| @ -129,6 +135,11 @@ static bool test_read(const char* file_name) { | |||||||
|         if(memcmp(scratchpad, test_float_data, sizeof(float) * COUNT_OF(test_float_data)) != 0) |         if(memcmp(scratchpad, test_float_data, sizeof(float) * COUNT_OF(test_float_data)) != 0) | ||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
|  |         if(!flipper_format_get_value_count(file, test_bool_key, &uint32_value)) break; | ||||||
|  |         if(uint32_value != COUNT_OF(test_bool_data)) break; | ||||||
|  |         if(!flipper_format_read_bool(file, test_bool_key, scratchpad, uint32_value)) break; | ||||||
|  |         if(memcmp(scratchpad, test_bool_data, sizeof(bool) * COUNT_OF(test_bool_data)) != 0) break; | ||||||
|  | 
 | ||||||
|         if(!flipper_format_get_value_count(file, test_hex_key, &uint32_value)) break; |         if(!flipper_format_get_value_count(file, test_hex_key, &uint32_value)) break; | ||||||
|         if(uint32_value != COUNT_OF(test_hex_data)) break; |         if(uint32_value != COUNT_OF(test_hex_data)) break; | ||||||
|         if(!flipper_format_read_hex(file, test_hex_key, scratchpad, uint32_value)) break; |         if(!flipper_format_read_hex(file, test_hex_key, scratchpad, uint32_value)) break; | ||||||
| @ -195,6 +206,15 @@ static bool test_read_updated(const char* file_name) { | |||||||
|                sizeof(float) * COUNT_OF(test_float_updated_data)) != 0) |                sizeof(float) * COUNT_OF(test_float_updated_data)) != 0) | ||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
|  |         if(!flipper_format_get_value_count(file, test_bool_key, &uint32_value)) break; | ||||||
|  |         if(uint32_value != COUNT_OF(test_bool_updated_data)) break; | ||||||
|  |         if(!flipper_format_read_bool(file, test_bool_key, scratchpad, uint32_value)) break; | ||||||
|  |         if(memcmp( | ||||||
|  |                scratchpad, | ||||||
|  |                test_bool_updated_data, | ||||||
|  |                sizeof(bool) * COUNT_OF(test_bool_updated_data)) != 0) | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|         if(!flipper_format_get_value_count(file, test_hex_key, &uint32_value)) break; |         if(!flipper_format_get_value_count(file, test_hex_key, &uint32_value)) break; | ||||||
|         if(uint32_value != COUNT_OF(test_hex_updated_data)) break; |         if(uint32_value != COUNT_OF(test_hex_updated_data)) break; | ||||||
|         if(!flipper_format_read_hex(file, test_hex_key, scratchpad, uint32_value)) break; |         if(!flipper_format_read_hex(file, test_hex_key, scratchpad, uint32_value)) break; | ||||||
| @ -235,6 +255,9 @@ static bool test_write(const char* file_name) { | |||||||
|         if(!flipper_format_write_float( |         if(!flipper_format_write_float( | ||||||
|                file, test_float_key, test_float_data, COUNT_OF(test_float_data))) |                file, test_float_key, test_float_data, COUNT_OF(test_float_data))) | ||||||
|             break; |             break; | ||||||
|  |         if(!flipper_format_write_bool( | ||||||
|  |                file, test_bool_key, test_bool_data, COUNT_OF(test_bool_data))) | ||||||
|  |             break; | ||||||
|         if(!flipper_format_write_hex(file, test_hex_key, test_hex_data, COUNT_OF(test_hex_data))) |         if(!flipper_format_write_hex(file, test_hex_key, test_hex_data, COUNT_OF(test_hex_data))) | ||||||
|             break; |             break; | ||||||
|         result = true; |         result = true; | ||||||
| @ -299,6 +322,9 @@ static bool test_update(const char* file_name) { | |||||||
|         if(!flipper_format_update_float( |         if(!flipper_format_update_float( | ||||||
|                file, test_float_key, test_float_updated_data, COUNT_OF(test_float_updated_data))) |                file, test_float_key, test_float_updated_data, COUNT_OF(test_float_updated_data))) | ||||||
|             break; |             break; | ||||||
|  |         if(!flipper_format_update_bool( | ||||||
|  |                file, test_bool_key, test_bool_updated_data, COUNT_OF(test_bool_updated_data))) | ||||||
|  |             break; | ||||||
|         if(!flipper_format_update_hex( |         if(!flipper_format_update_hex( | ||||||
|                file, test_hex_key, test_hex_updated_data, COUNT_OF(test_hex_updated_data))) |                file, test_hex_key, test_hex_updated_data, COUNT_OF(test_hex_updated_data))) | ||||||
|             break; |             break; | ||||||
| @ -328,6 +354,9 @@ static bool test_update_backward(const char* file_name) { | |||||||
|         if(!flipper_format_update_float( |         if(!flipper_format_update_float( | ||||||
|                file, test_float_key, test_float_data, COUNT_OF(test_float_data))) |                file, test_float_key, test_float_data, COUNT_OF(test_float_data))) | ||||||
|             break; |             break; | ||||||
|  |         if(!flipper_format_update_bool( | ||||||
|  |                file, test_bool_key, test_bool_data, COUNT_OF(test_bool_data))) | ||||||
|  |             break; | ||||||
|         if(!flipper_format_update_hex(file, test_hex_key, test_hex_data, COUNT_OF(test_hex_data))) |         if(!flipper_format_update_hex(file, test_hex_key, test_hex_data, COUNT_OF(test_hex_data))) | ||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -246,6 +246,36 @@ bool flipper_format_write_int32( | |||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool flipper_format_read_bool( | ||||||
|  |     FlipperFormat* flipper_format, | ||||||
|  |     const char* key, | ||||||
|  |     bool* data, | ||||||
|  |     const uint16_t data_size) { | ||||||
|  |     return flipper_format_stream_read_value_line( | ||||||
|  |         flipper_format->stream, | ||||||
|  |         key, | ||||||
|  |         FlipperStreamValueBool, | ||||||
|  |         data, | ||||||
|  |         data_size, | ||||||
|  |         flipper_format->strict_mode); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool flipper_format_write_bool( | ||||||
|  |     FlipperFormat* flipper_format, | ||||||
|  |     const char* key, | ||||||
|  |     const bool* data, | ||||||
|  |     const uint16_t data_size) { | ||||||
|  |     furi_assert(flipper_format); | ||||||
|  |     FlipperStreamWriteData write_data = { | ||||||
|  |         .key = key, | ||||||
|  |         .type = FlipperStreamValueBool, | ||||||
|  |         .data = data, | ||||||
|  |         .data_size = data_size, | ||||||
|  |     }; | ||||||
|  |     bool result = flipper_format_stream_write_value_line(flipper_format->stream, &write_data); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool flipper_format_read_float( | bool flipper_format_read_float( | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     const char* key, |     const char* key, | ||||||
| @ -391,6 +421,22 @@ bool flipper_format_update_int32( | |||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool flipper_format_update_bool( | ||||||
|  |     FlipperFormat* flipper_format, | ||||||
|  |     const char* key, | ||||||
|  |     const bool* data, | ||||||
|  |     const uint16_t data_size) { | ||||||
|  |     FlipperStreamWriteData write_data = { | ||||||
|  |         .key = key, | ||||||
|  |         .type = FlipperStreamValueBool, | ||||||
|  |         .data = data, | ||||||
|  |         .data_size = data_size, | ||||||
|  |     }; | ||||||
|  |     bool result = flipper_format_stream_delete_key_and_write( | ||||||
|  |         flipper_format->stream, &write_data, flipper_format->strict_mode); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool flipper_format_update_float( | bool flipper_format_update_float( | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     const char* key, |     const char* key, | ||||||
| @ -489,6 +535,23 @@ bool flipper_format_insert_or_update_int32( | |||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool flipper_format_insert_or_update_bool( | ||||||
|  |     FlipperFormat* flipper_format, | ||||||
|  |     const char* key, | ||||||
|  |     const bool* data, | ||||||
|  |     const uint16_t data_size) { | ||||||
|  |     bool result = false; | ||||||
|  | 
 | ||||||
|  |     if(!flipper_format_key_exist(flipper_format, key)) { | ||||||
|  |         flipper_format_seek_to_end(flipper_format); | ||||||
|  |         result = flipper_format_write_bool(flipper_format, key, data, data_size); | ||||||
|  |     } else { | ||||||
|  |         result = flipper_format_update_bool(flipper_format, key, data, data_size); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool flipper_format_insert_or_update_float( | bool flipper_format_insert_or_update_float( | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     const char* key, |     const char* key, | ||||||
|  | |||||||
| @ -329,6 +329,34 @@ bool flipper_format_write_int32( | |||||||
|     const int32_t* data, |     const int32_t* data, | ||||||
|     const uint16_t data_size); |     const uint16_t data_size); | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * Read array of bool by key | ||||||
|  |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  |  * @param key Key | ||||||
|  |  * @param data Value | ||||||
|  |  * @param data_size Values count | ||||||
|  |  * @return True on success | ||||||
|  |  */ | ||||||
|  | bool flipper_format_read_bool( | ||||||
|  |     FlipperFormat* flipper_format, | ||||||
|  |     const char* key, | ||||||
|  |     bool* data, | ||||||
|  |     const uint16_t data_size); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Write key and array of bool | ||||||
|  |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  |  * @param key Key | ||||||
|  |  * @param data Value | ||||||
|  |  * @param data_size Values count | ||||||
|  |  * @return True on success | ||||||
|  |  */ | ||||||
|  | bool flipper_format_write_bool( | ||||||
|  |     FlipperFormat* flipper_format, | ||||||
|  |     const char* key, | ||||||
|  |     const bool* data, | ||||||
|  |     const uint16_t data_size); | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Read array of float by key |  * Read array of float by key | ||||||
|  * @param flipper_format Pointer to a FlipperFormat instance |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
| @ -456,6 +484,19 @@ bool flipper_format_update_int32( | |||||||
|     const int32_t* data, |     const int32_t* data, | ||||||
|     const uint16_t data_size); |     const uint16_t data_size); | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * Updates the value of the first matching key to a bool array value. Sets the RW pointer to a position at the end of inserted data. | ||||||
|  |  * @param flipper_format Pointer to a FlipperFormat instance  | ||||||
|  |  * @param key Key | ||||||
|  |  * @param data Value | ||||||
|  |  * @return True on success | ||||||
|  |  */ | ||||||
|  | bool flipper_format_update_bool( | ||||||
|  |     FlipperFormat* flipper_format, | ||||||
|  |     const char* key, | ||||||
|  |     const bool* data, | ||||||
|  |     const uint16_t data_size); | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Updates the value of the first matching key to a float array value. Sets the RW pointer to a position at the end of inserted data. |  * Updates the value of the first matching key to a float array value. Sets the RW pointer to a position at the end of inserted data. | ||||||
|  * @param flipper_format Pointer to a FlipperFormat instance  |  * @param flipper_format Pointer to a FlipperFormat instance  | ||||||
| @ -537,6 +578,20 @@ bool flipper_format_insert_or_update_int32( | |||||||
|     const int32_t* data, |     const int32_t* data, | ||||||
|     const uint16_t data_size); |     const uint16_t data_size); | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * Updates the value of the first matching key to a bool array value, or adds the key and value if the key did not exist.  | ||||||
|  |  * Sets the RW pointer to a position at the end of inserted data. | ||||||
|  |  * @param flipper_format Pointer to a FlipperFormat instance  | ||||||
|  |  * @param key Key | ||||||
|  |  * @param data Value | ||||||
|  |  * @return True on success | ||||||
|  |  */ | ||||||
|  | bool flipper_format_insert_or_update_bool( | ||||||
|  |     FlipperFormat* flipper_format, | ||||||
|  |     const char* key, | ||||||
|  |     const bool* data, | ||||||
|  |     const uint16_t data_size); | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Updates the value of the first matching key to a float array value, or adds the key and value if the key did not exist.  |  * Updates the value of the first matching key to a float array value, or adds the key and value if the key did not exist.  | ||||||
|  * Sets the RW pointer to a position at the end of inserted data. |  * Sets the RW pointer to a position at the end of inserted data. | ||||||
|  | |||||||
| @ -285,6 +285,10 @@ bool flipper_format_stream_write_value_line(Stream* stream, FlipperStreamWriteDa | |||||||
|                     const uint32_t* data = write_data->data; |                     const uint32_t* data = write_data->data; | ||||||
|                     string_printf(value, "%" PRId32, data[i]); |                     string_printf(value, "%" PRId32, data[i]); | ||||||
|                 }; break; |                 }; break; | ||||||
|  |                 case FlipperStreamValueBool: { | ||||||
|  |                     const bool* data = write_data->data; | ||||||
|  |                     string_printf(value, data[i] ? "true" : "false"); | ||||||
|  |                 }; break; | ||||||
|                 default: |                 default: | ||||||
|                     furi_crash("Unknown FF type"); |                     furi_crash("Unknown FF type"); | ||||||
|                 } |                 } | ||||||
| @ -372,6 +376,11 @@ bool flipper_format_stream_read_value_line( | |||||||
|                         uint32_t* data = _data; |                         uint32_t* data = _data; | ||||||
|                         scan_values = sscanf(string_get_cstr(value), "%" PRId32, &data[i]); |                         scan_values = sscanf(string_get_cstr(value), "%" PRId32, &data[i]); | ||||||
|                     }; break; |                     }; break; | ||||||
|  |                     case FlipperStreamValueBool: { | ||||||
|  |                         bool* data = _data; | ||||||
|  |                         data[i] = !string_cmpi_str(value, "true"); | ||||||
|  |                         scan_values = 1; | ||||||
|  |                     }; break; | ||||||
|                     default: |                     default: | ||||||
|                         furi_crash("Unknown FF type"); |                         furi_crash("Unknown FF type"); | ||||||
|                     } |                     } | ||||||
|  | |||||||
| @ -15,6 +15,7 @@ typedef enum { | |||||||
|     FlipperStreamValueFloat, |     FlipperStreamValueFloat, | ||||||
|     FlipperStreamValueInt32, |     FlipperStreamValueInt32, | ||||||
|     FlipperStreamValueUint32, |     FlipperStreamValueUint32, | ||||||
|  |     FlipperStreamValueBool, | ||||||
| } FlipperStreamValue; | } FlipperStreamValue; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|  | |||||||
							
								
								
									
										447
									
								
								lib/nfc_protocols/mifare_desfire.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										447
									
								
								lib/nfc_protocols/mifare_desfire.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,447 @@ | |||||||
|  | #include "mifare_desfire.h" | ||||||
|  | #include <furi.h> | ||||||
|  | #include <furi_hal_nfc.h> | ||||||
|  | 
 | ||||||
|  | void mf_df_clear(MifareDesfireData* data) { | ||||||
|  |     free(data->free_memory); | ||||||
|  |     if(data->master_key_settings) { | ||||||
|  |         MifareDesfireKeyVersion* key_version = data->master_key_settings->key_version_head; | ||||||
|  |         while(key_version) { | ||||||
|  |             MifareDesfireKeyVersion* next_key_version = key_version->next; | ||||||
|  |             free(key_version); | ||||||
|  |             key_version = next_key_version; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     free(data->master_key_settings); | ||||||
|  |     MifareDesfireApplication* app = data->app_head; | ||||||
|  |     while(app) { | ||||||
|  |         MifareDesfireApplication* next_app = app->next; | ||||||
|  |         if(app->key_settings) { | ||||||
|  |             MifareDesfireKeyVersion* key_version = app->key_settings->key_version_head; | ||||||
|  |             while(key_version) { | ||||||
|  |                 MifareDesfireKeyVersion* next_key_version = key_version->next; | ||||||
|  |                 free(key_version); | ||||||
|  |                 key_version = next_key_version; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         free(app->key_settings); | ||||||
|  |         MifareDesfireFile* file = app->file_head; | ||||||
|  |         while(file) { | ||||||
|  |             MifareDesfireFile* next_file = file->next; | ||||||
|  |             free(file->contents); | ||||||
|  |             free(file); | ||||||
|  |             file = next_file; | ||||||
|  |         } | ||||||
|  |         free(app); | ||||||
|  |         app = next_app; | ||||||
|  |     } | ||||||
|  |     data->free_memory = NULL; | ||||||
|  |     data->master_key_settings = NULL; | ||||||
|  |     data->app_head = NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void mf_df_cat_data(MifareDesfireData* data, string_t out) { | ||||||
|  |     mf_df_cat_card_info(data, out); | ||||||
|  |     for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { | ||||||
|  |         mf_df_cat_application(app, out); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void mf_df_cat_card_info(MifareDesfireData* data, string_t out) { | ||||||
|  |     mf_df_cat_version(&data->version, out); | ||||||
|  |     if(data->free_memory) { | ||||||
|  |         mf_df_cat_free_mem(data->free_memory, out); | ||||||
|  |     } | ||||||
|  |     if(data->master_key_settings) { | ||||||
|  |         mf_df_cat_key_settings(data->master_key_settings, out); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void mf_df_cat_version(MifareDesfireVersion* version, string_t out) { | ||||||
|  |     string_cat_printf( | ||||||
|  |         out, | ||||||
|  |         "%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", | ||||||
|  |         version->uid[0], | ||||||
|  |         version->uid[1], | ||||||
|  |         version->uid[2], | ||||||
|  |         version->uid[3], | ||||||
|  |         version->uid[4], | ||||||
|  |         version->uid[5], | ||||||
|  |         version->uid[6]); | ||||||
|  |     string_cat_printf( | ||||||
|  |         out, | ||||||
|  |         "hw %02x type %02x sub %02x\n" | ||||||
|  |         " maj %02x min %02x\n" | ||||||
|  |         " size %02x proto %02x\n", | ||||||
|  |         version->hw_vendor, | ||||||
|  |         version->hw_type, | ||||||
|  |         version->hw_subtype, | ||||||
|  |         version->hw_major, | ||||||
|  |         version->hw_minor, | ||||||
|  |         version->hw_storage, | ||||||
|  |         version->hw_proto); | ||||||
|  |     string_cat_printf( | ||||||
|  |         out, | ||||||
|  |         "sw %02x type %02x sub %02x\n" | ||||||
|  |         " maj %02x min %02x\n" | ||||||
|  |         " size %02x proto %02x\n", | ||||||
|  |         version->sw_vendor, | ||||||
|  |         version->sw_type, | ||||||
|  |         version->sw_subtype, | ||||||
|  |         version->sw_major, | ||||||
|  |         version->sw_minor, | ||||||
|  |         version->sw_storage, | ||||||
|  |         version->sw_proto); | ||||||
|  |     string_cat_printf( | ||||||
|  |         out, | ||||||
|  |         "batch %02x:%02x:%02x:%02x:%02x\n" | ||||||
|  |         "week %d year %d\n", | ||||||
|  |         version->batch[0], | ||||||
|  |         version->batch[1], | ||||||
|  |         version->batch[2], | ||||||
|  |         version->batch[3], | ||||||
|  |         version->batch[4], | ||||||
|  |         version->prod_week, | ||||||
|  |         version->prod_year); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void mf_df_cat_free_mem(MifareDesfireFreeMemory* free_mem, string_t out) { | ||||||
|  |     string_cat_printf(out, "freeMem %d\n", free_mem->bytes); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void mf_df_cat_key_settings(MifareDesfireKeySettings* ks, string_t out) { | ||||||
|  |     string_cat_printf(out, "changeKeyID %d\n", ks->change_key_id); | ||||||
|  |     string_cat_printf(out, "configChangeable %d\n", ks->config_changeable); | ||||||
|  |     string_cat_printf(out, "freeCreateDelete %d\n", ks->free_create_delete); | ||||||
|  |     string_cat_printf(out, "freeDirectoryList %d\n", ks->free_directory_list); | ||||||
|  |     string_cat_printf(out, "masterChangeable %d\n", ks->master_key_changeable); | ||||||
|  |     string_cat_printf(out, "maxKeys %d\n", ks->max_keys); | ||||||
|  |     for(MifareDesfireKeyVersion* kv = ks->key_version_head; kv; kv = kv->next) { | ||||||
|  |         string_cat_printf(out, "key %d version %d\n", kv->id, kv->version); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void mf_df_cat_application_info(MifareDesfireApplication* app, string_t out) { | ||||||
|  |     string_cat_printf(out, "Application %02x%02x%02x\n", app->id[0], app->id[1], app->id[2]); | ||||||
|  |     if(app->key_settings) { | ||||||
|  |         mf_df_cat_key_settings(app->key_settings, out); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void mf_df_cat_application(MifareDesfireApplication* app, string_t out) { | ||||||
|  |     mf_df_cat_application_info(app, out); | ||||||
|  |     for(MifareDesfireFile* file = app->file_head; file; file = file->next) { | ||||||
|  |         mf_df_cat_file(file, out); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void mf_df_cat_file(MifareDesfireFile* file, string_t out) { | ||||||
|  |     char* type = "unknown"; | ||||||
|  |     switch(file->type) { | ||||||
|  |     case MifareDesfireFileTypeStandard: | ||||||
|  |         type = "standard"; | ||||||
|  |         break; | ||||||
|  |     case MifareDesfireFileTypeBackup: | ||||||
|  |         type = "backup"; | ||||||
|  |         break; | ||||||
|  |     case MifareDesfireFileTypeValue: | ||||||
|  |         type = "value"; | ||||||
|  |         break; | ||||||
|  |     case MifareDesfireFileTypeLinearRecord: | ||||||
|  |         type = "linear"; | ||||||
|  |         break; | ||||||
|  |     case MifareDesfireFileTypeCyclicRecord: | ||||||
|  |         type = "cyclic"; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     char* comm = "unknown"; | ||||||
|  |     switch(file->comm) { | ||||||
|  |     case MifareDesfireFileCommunicationSettingsPlaintext: | ||||||
|  |         comm = "plain"; | ||||||
|  |         break; | ||||||
|  |     case MifareDesfireFileCommunicationSettingsAuthenticated: | ||||||
|  |         comm = "auth"; | ||||||
|  |         break; | ||||||
|  |     case MifareDesfireFileCommunicationSettingsEnciphered: | ||||||
|  |         comm = "enciphered"; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     string_cat_printf(out, "File %d\n", file->id); | ||||||
|  |     string_cat_printf(out, "%s %s\n", type, comm); | ||||||
|  |     string_cat_printf( | ||||||
|  |         out, | ||||||
|  |         "r %d w %d rw %d c %d\n", | ||||||
|  |         file->access_rights >> 12 & 0xF, | ||||||
|  |         file->access_rights >> 8 & 0xF, | ||||||
|  |         file->access_rights >> 4 & 0xF, | ||||||
|  |         file->access_rights & 0xF); | ||||||
|  |     uint16_t size = 0; | ||||||
|  |     uint16_t num = 1; | ||||||
|  |     switch(file->type) { | ||||||
|  |     case MifareDesfireFileTypeStandard: | ||||||
|  |     case MifareDesfireFileTypeBackup: | ||||||
|  |         size = file->settings.data.size; | ||||||
|  |         string_cat_printf(out, "size %d\n", size); | ||||||
|  |         break; | ||||||
|  |     case MifareDesfireFileTypeValue: | ||||||
|  |         size = 4; | ||||||
|  |         string_cat_printf( | ||||||
|  |             out, "lo %d hi %d\n", file->settings.value.lo_limit, file->settings.value.hi_limit); | ||||||
|  |         string_cat_printf( | ||||||
|  |             out, | ||||||
|  |             "limit %d enabled %d\n", | ||||||
|  |             file->settings.value.limited_credit_value, | ||||||
|  |             file->settings.value.limited_credit_enabled); | ||||||
|  |         break; | ||||||
|  |     case MifareDesfireFileTypeLinearRecord: | ||||||
|  |     case MifareDesfireFileTypeCyclicRecord: | ||||||
|  |         size = file->settings.record.size; | ||||||
|  |         num = file->settings.record.cur; | ||||||
|  |         string_cat_printf(out, "size %d\n", size); | ||||||
|  |         string_cat_printf(out, "num %d max %d\n", num, file->settings.record.max); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     uint8_t* data = file->contents; | ||||||
|  |     if(data) { | ||||||
|  |         for(int rec = 0; rec < num; rec++) { | ||||||
|  |             for(int ch = 0; ch < size; ch++) { | ||||||
|  |                 string_cat_printf(out, "%02x", data[rec * size + ch]); | ||||||
|  |             } | ||||||
|  |             string_cat_printf(out, " \n"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool mf_df_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { | ||||||
|  |     return ATQA0 == 0x44 && ATQA1 == 0x03 && SAK == 0x20; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint16_t mf_df_prepare_get_version(uint8_t* dest) { | ||||||
|  |     dest[0] = MF_DF_GET_VERSION; | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool mf_df_parse_get_version_response(uint8_t* buf, uint16_t len, MifareDesfireVersion* out) { | ||||||
|  |     if(len < 1 || *buf) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     len--; | ||||||
|  |     buf++; | ||||||
|  |     if(len < sizeof(MifareDesfireVersion)) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     memcpy(out, buf, sizeof(MifareDesfireVersion)); | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint16_t mf_df_prepare_get_free_memory(uint8_t* dest) { | ||||||
|  |     dest[0] = MF_DF_GET_FREE_MEMORY; | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool mf_df_parse_get_free_memory_response(uint8_t* buf, uint16_t len, MifareDesfireFreeMemory* out) { | ||||||
|  |     if(len < 1 || *buf) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     len--; | ||||||
|  |     buf++; | ||||||
|  |     if(len != 3) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     out->bytes = buf[0] | (buf[1] << 8) | (buf[2] << 16); | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint16_t mf_df_prepare_get_key_settings(uint8_t* dest) { | ||||||
|  |     dest[0] = MF_DF_GET_KEY_SETTINGS; | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool mf_df_parse_get_key_settings_response( | ||||||
|  |     uint8_t* buf, | ||||||
|  |     uint16_t len, | ||||||
|  |     MifareDesfireKeySettings* out) { | ||||||
|  |     if(len < 1 || *buf) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     len--; | ||||||
|  |     buf++; | ||||||
|  |     if(len < 2) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     out->change_key_id = buf[0] >> 4; | ||||||
|  |     out->config_changeable = (buf[0] & 0x8) != 0; | ||||||
|  |     out->free_create_delete = (buf[0] & 0x4) != 0; | ||||||
|  |     out->free_directory_list = (buf[0] & 0x2) != 0; | ||||||
|  |     out->master_key_changeable = (buf[0] & 0x1) != 0; | ||||||
|  |     out->max_keys = buf[1]; | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint16_t mf_df_prepare_get_key_version(uint8_t* dest, uint8_t key_id) { | ||||||
|  |     dest[0] = MF_DF_GET_KEY_VERSION; | ||||||
|  |     dest[1] = key_id; | ||||||
|  |     return 2; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool mf_df_parse_get_key_version_response(uint8_t* buf, uint16_t len, MifareDesfireKeyVersion* out) { | ||||||
|  |     if(len != 2 || *buf) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     out->version = buf[1]; | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint16_t mf_df_prepare_get_application_ids(uint8_t* dest) { | ||||||
|  |     dest[0] = MF_DF_GET_APPLICATION_IDS; | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool mf_df_parse_get_application_ids_response( | ||||||
|  |     uint8_t* buf, | ||||||
|  |     uint16_t len, | ||||||
|  |     MifareDesfireApplication** app_head) { | ||||||
|  |     if(len < 1 || *buf) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     len--; | ||||||
|  |     buf++; | ||||||
|  |     if(len % 3 != 0) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     while(len) { | ||||||
|  |         MifareDesfireApplication* app = malloc(sizeof(MifareDesfireApplication)); | ||||||
|  |         memset(app, 0, sizeof(MifareDesfireApplication)); | ||||||
|  |         memcpy(app->id, buf, 3); | ||||||
|  |         len -= 3; | ||||||
|  |         buf += 3; | ||||||
|  |         *app_head = app; | ||||||
|  |         app_head = &app->next; | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint16_t mf_df_prepare_select_application(uint8_t* dest, uint8_t id[3]) { | ||||||
|  |     dest[0] = MF_DF_SELECT_APPLICATION; | ||||||
|  |     dest[1] = id[0]; | ||||||
|  |     dest[2] = id[1]; | ||||||
|  |     dest[3] = id[2]; | ||||||
|  |     return 4; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool mf_df_parse_select_application_response(uint8_t* buf, uint16_t len) { | ||||||
|  |     return len == 1 && !*buf; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint16_t mf_df_prepare_get_file_ids(uint8_t* dest) { | ||||||
|  |     dest[0] = MF_DF_GET_FILE_IDS; | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool mf_df_parse_get_file_ids_response(uint8_t* buf, uint16_t len, MifareDesfireFile** file_head) { | ||||||
|  |     if(len < 1 || *buf) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     len--; | ||||||
|  |     buf++; | ||||||
|  |     while(len) { | ||||||
|  |         MifareDesfireFile* file = malloc(sizeof(MifareDesfireFile)); | ||||||
|  |         memset(file, 0, sizeof(MifareDesfireFile)); | ||||||
|  |         file->id = *buf; | ||||||
|  |         len--; | ||||||
|  |         buf++; | ||||||
|  |         *file_head = file; | ||||||
|  |         file_head = &file->next; | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint16_t mf_df_prepare_get_file_settings(uint8_t* dest, uint8_t file_id) { | ||||||
|  |     dest[0] = MF_DF_GET_FILE_SETTINGS; | ||||||
|  |     dest[1] = file_id; | ||||||
|  |     return 2; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool mf_df_parse_get_file_settings_response(uint8_t* buf, uint16_t len, MifareDesfireFile* out) { | ||||||
|  |     if(len < 5 || *buf) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     len--; | ||||||
|  |     buf++; | ||||||
|  |     out->type = buf[0]; | ||||||
|  |     out->comm = buf[1]; | ||||||
|  |     out->access_rights = buf[2] | (buf[3] << 8); | ||||||
|  |     switch(out->type) { | ||||||
|  |     case MifareDesfireFileTypeStandard: | ||||||
|  |     case MifareDesfireFileTypeBackup: | ||||||
|  |         if(len != 7) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         out->settings.data.size = buf[4] | (buf[5] << 8) | (buf[6] << 16); | ||||||
|  |         break; | ||||||
|  |     case MifareDesfireFileTypeValue: | ||||||
|  |         if(len != 17) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         out->settings.value.lo_limit = buf[4] | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 24); | ||||||
|  |         out->settings.value.hi_limit = buf[8] | (buf[9] << 8) | (buf[10] << 16) | (buf[11] << 24); | ||||||
|  |         out->settings.value.limited_credit_value = buf[12] | (buf[13] << 8) | (buf[14] << 16) | | ||||||
|  |                                                    (buf[15] << 24); | ||||||
|  |         out->settings.value.limited_credit_enabled = buf[16]; | ||||||
|  |         break; | ||||||
|  |     case MifareDesfireFileTypeLinearRecord: | ||||||
|  |     case MifareDesfireFileTypeCyclicRecord: | ||||||
|  |         if(len != 13) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         out->settings.record.size = buf[4] | (buf[5] << 8) | (buf[6] << 16); | ||||||
|  |         out->settings.record.max = buf[7] | (buf[8] << 8) | (buf[9] << 16); | ||||||
|  |         out->settings.record.cur = buf[10] | (buf[11] << 8) | (buf[12] << 16); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint16_t mf_df_prepare_read_data(uint8_t* dest, uint8_t file_id, uint32_t offset, uint32_t len) { | ||||||
|  |     dest[0] = MF_DF_READ_DATA; | ||||||
|  |     dest[1] = file_id; | ||||||
|  |     dest[2] = offset; | ||||||
|  |     dest[3] = offset >> 8; | ||||||
|  |     dest[4] = offset >> 16; | ||||||
|  |     dest[5] = len; | ||||||
|  |     dest[6] = len >> 8; | ||||||
|  |     dest[7] = len >> 16; | ||||||
|  |     return 8; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint16_t mf_df_prepare_get_value(uint8_t* dest, uint8_t file_id) { | ||||||
|  |     dest[0] = MF_DF_GET_VALUE; | ||||||
|  |     dest[1] = file_id; | ||||||
|  |     return 2; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint16_t | ||||||
|  |     mf_df_prepare_read_records(uint8_t* dest, uint8_t file_id, uint32_t offset, uint32_t len) { | ||||||
|  |     dest[0] = MF_DF_READ_RECORDS; | ||||||
|  |     dest[1] = file_id; | ||||||
|  |     dest[2] = offset; | ||||||
|  |     dest[3] = offset >> 8; | ||||||
|  |     dest[4] = offset >> 16; | ||||||
|  |     dest[5] = len; | ||||||
|  |     dest[6] = len >> 8; | ||||||
|  |     dest[7] = len >> 16; | ||||||
|  |     return 8; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool mf_df_parse_read_data_response(uint8_t* buf, uint16_t len, MifareDesfireFile* out) { | ||||||
|  |     if(len < 1 || *buf) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     len--; | ||||||
|  |     buf++; | ||||||
|  |     out->contents = malloc(len); | ||||||
|  |     memcpy(out->contents, buf, len); | ||||||
|  |     return true; | ||||||
|  | } | ||||||
							
								
								
									
										164
									
								
								lib/nfc_protocols/mifare_desfire.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								lib/nfc_protocols/mifare_desfire.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,164 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <m-string.h> | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | 
 | ||||||
|  | #define MF_DF_GET_VERSION (0x60) | ||||||
|  | #define MF_DF_GET_FREE_MEMORY (0x6E) | ||||||
|  | #define MF_DF_GET_KEY_SETTINGS (0x45) | ||||||
|  | #define MF_DF_GET_KEY_VERSION (0x64) | ||||||
|  | #define MF_DF_GET_APPLICATION_IDS (0x6A) | ||||||
|  | #define MF_DF_SELECT_APPLICATION (0x5A) | ||||||
|  | #define MF_DF_GET_FILE_IDS (0x6F) | ||||||
|  | #define MF_DF_GET_FILE_SETTINGS (0xF5) | ||||||
|  | 
 | ||||||
|  | #define MF_DF_READ_DATA (0xBD) | ||||||
|  | #define MF_DF_GET_VALUE (0x6C) | ||||||
|  | #define MF_DF_READ_RECORDS (0xBB) | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     uint8_t hw_vendor; | ||||||
|  |     uint8_t hw_type; | ||||||
|  |     uint8_t hw_subtype; | ||||||
|  |     uint8_t hw_major; | ||||||
|  |     uint8_t hw_minor; | ||||||
|  |     uint8_t hw_storage; | ||||||
|  |     uint8_t hw_proto; | ||||||
|  | 
 | ||||||
|  |     uint8_t sw_vendor; | ||||||
|  |     uint8_t sw_type; | ||||||
|  |     uint8_t sw_subtype; | ||||||
|  |     uint8_t sw_major; | ||||||
|  |     uint8_t sw_minor; | ||||||
|  |     uint8_t sw_storage; | ||||||
|  |     uint8_t sw_proto; | ||||||
|  | 
 | ||||||
|  |     uint8_t uid[7]; | ||||||
|  |     uint8_t batch[5]; | ||||||
|  |     uint8_t prod_week; | ||||||
|  |     uint8_t prod_year; | ||||||
|  | } MifareDesfireVersion; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     uint32_t bytes; | ||||||
|  | } MifareDesfireFreeMemory; // EV1+ only
 | ||||||
|  | 
 | ||||||
|  | typedef struct MifareDesfireKeyVersion { | ||||||
|  |     uint8_t id; | ||||||
|  |     uint8_t version; | ||||||
|  |     struct MifareDesfireKeyVersion* next; | ||||||
|  | } MifareDesfireKeyVersion; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     uint8_t change_key_id; | ||||||
|  |     bool config_changeable; | ||||||
|  |     bool free_create_delete; | ||||||
|  |     bool free_directory_list; | ||||||
|  |     bool master_key_changeable; | ||||||
|  |     uint8_t max_keys; | ||||||
|  |     MifareDesfireKeyVersion* key_version_head; | ||||||
|  | } MifareDesfireKeySettings; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     MifareDesfireFileTypeStandard = 0, | ||||||
|  |     MifareDesfireFileTypeBackup = 1, | ||||||
|  |     MifareDesfireFileTypeValue = 2, | ||||||
|  |     MifareDesfireFileTypeLinearRecord = 3, | ||||||
|  |     MifareDesfireFileTypeCyclicRecord = 4, | ||||||
|  | } MifareDesfireFileType; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     MifareDesfireFileCommunicationSettingsPlaintext = 0, | ||||||
|  |     MifareDesfireFileCommunicationSettingsAuthenticated = 1, | ||||||
|  |     MifareDesfireFileCommunicationSettingsEnciphered = 3, | ||||||
|  | } MifareDesfireFileCommunicationSettings; | ||||||
|  | 
 | ||||||
|  | typedef struct MifareDesfireFile { | ||||||
|  |     uint8_t id; | ||||||
|  |     MifareDesfireFileType type; | ||||||
|  |     MifareDesfireFileCommunicationSettings comm; | ||||||
|  |     uint16_t access_rights; | ||||||
|  |     union { | ||||||
|  |         struct { | ||||||
|  |             uint32_t size; | ||||||
|  |         } data; | ||||||
|  |         struct { | ||||||
|  |             uint32_t lo_limit; | ||||||
|  |             uint32_t hi_limit; | ||||||
|  |             uint32_t limited_credit_value; | ||||||
|  |             bool limited_credit_enabled; | ||||||
|  |         } value; | ||||||
|  |         struct { | ||||||
|  |             uint32_t size; | ||||||
|  |             uint32_t max; | ||||||
|  |             uint32_t cur; | ||||||
|  |         } record; | ||||||
|  |     } settings; | ||||||
|  |     uint8_t* contents; | ||||||
|  | 
 | ||||||
|  |     struct MifareDesfireFile* next; | ||||||
|  | } MifareDesfireFile; | ||||||
|  | 
 | ||||||
|  | typedef struct MifareDesfireApplication { | ||||||
|  |     uint8_t id[3]; | ||||||
|  |     MifareDesfireKeySettings* key_settings; | ||||||
|  |     MifareDesfireFile* file_head; | ||||||
|  | 
 | ||||||
|  |     struct MifareDesfireApplication* next; | ||||||
|  | } MifareDesfireApplication; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     MifareDesfireVersion version; | ||||||
|  |     MifareDesfireFreeMemory* free_memory; | ||||||
|  |     MifareDesfireKeySettings* master_key_settings; | ||||||
|  |     MifareDesfireApplication* app_head; | ||||||
|  | } MifareDesfireData; | ||||||
|  | 
 | ||||||
|  | void mf_df_clear(MifareDesfireData* data); | ||||||
|  | 
 | ||||||
|  | void mf_df_cat_data(MifareDesfireData* data, string_t out); | ||||||
|  | void mf_df_cat_card_info(MifareDesfireData* data, string_t out); | ||||||
|  | void mf_df_cat_version(MifareDesfireVersion* version, string_t out); | ||||||
|  | void mf_df_cat_free_mem(MifareDesfireFreeMemory* free_mem, string_t out); | ||||||
|  | void mf_df_cat_key_settings(MifareDesfireKeySettings* ks, string_t out); | ||||||
|  | void mf_df_cat_application_info(MifareDesfireApplication* app, string_t out); | ||||||
|  | void mf_df_cat_application(MifareDesfireApplication* app, string_t out); | ||||||
|  | void mf_df_cat_file(MifareDesfireFile* file, string_t out); | ||||||
|  | 
 | ||||||
|  | bool mf_df_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK); | ||||||
|  | 
 | ||||||
|  | uint16_t mf_df_prepare_get_version(uint8_t* dest); | ||||||
|  | bool mf_df_parse_get_version_response(uint8_t* buf, uint16_t len, MifareDesfireVersion* out); | ||||||
|  | 
 | ||||||
|  | uint16_t mf_df_prepare_get_free_memory(uint8_t* dest); | ||||||
|  | bool mf_df_parse_get_free_memory_response(uint8_t* buf, uint16_t len, MifareDesfireFreeMemory* out); | ||||||
|  | 
 | ||||||
|  | uint16_t mf_df_prepare_get_key_settings(uint8_t* dest); | ||||||
|  | bool mf_df_parse_get_key_settings_response( | ||||||
|  |     uint8_t* buf, | ||||||
|  |     uint16_t len, | ||||||
|  |     MifareDesfireKeySettings* out); | ||||||
|  | 
 | ||||||
|  | uint16_t mf_df_prepare_get_key_version(uint8_t* dest, uint8_t key_id); | ||||||
|  | bool mf_df_parse_get_key_version_response(uint8_t* buf, uint16_t len, MifareDesfireKeyVersion* out); | ||||||
|  | 
 | ||||||
|  | uint16_t mf_df_prepare_get_application_ids(uint8_t* dest); | ||||||
|  | bool mf_df_parse_get_application_ids_response( | ||||||
|  |     uint8_t* buf, | ||||||
|  |     uint16_t len, | ||||||
|  |     MifareDesfireApplication** app_head); | ||||||
|  | 
 | ||||||
|  | uint16_t mf_df_prepare_select_application(uint8_t* dest, uint8_t id[3]); | ||||||
|  | bool mf_df_parse_select_application_response(uint8_t* buf, uint16_t len); | ||||||
|  | 
 | ||||||
|  | uint16_t mf_df_prepare_get_file_ids(uint8_t* dest); | ||||||
|  | bool mf_df_parse_get_file_ids_response(uint8_t* buf, uint16_t len, MifareDesfireFile** file_head); | ||||||
|  | 
 | ||||||
|  | uint16_t mf_df_prepare_get_file_settings(uint8_t* dest, uint8_t file_id); | ||||||
|  | bool mf_df_parse_get_file_settings_response(uint8_t* buf, uint16_t len, MifareDesfireFile* out); | ||||||
|  | 
 | ||||||
|  | uint16_t mf_df_prepare_read_data(uint8_t* dest, uint8_t file_id, uint32_t offset, uint32_t len); | ||||||
|  | uint16_t mf_df_prepare_get_value(uint8_t* dest, uint8_t file_id); | ||||||
|  | uint16_t mf_df_prepare_read_records(uint8_t* dest, uint8_t file_id, uint32_t offset, uint32_t len); | ||||||
|  | bool mf_df_parse_read_data_response(uint8_t* buf, uint16_t len, MifareDesfireFile* out); | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Kevin Wallace
						Kevin Wallace