[FL-662] Read Mifare Ultralight (#518)
* nfc: add read mifare ultralight to menu * emv_decoder: add pragma once * nfc: add mifare ultralight reader * nfc: add mifare ultralight read draw * nfc: add mifare ultralight type checker * nfc: rework menu callback * mifare ultralight: change type names Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									0b14db4fb3
								
							
						
					
					
						commit
						a0fdc559c9
					
				| @ -22,17 +22,7 @@ uint32_t nfc_view_exit(void* context) { | |||||||
| void nfc_menu_callback(void* context, uint32_t index) { | void nfc_menu_callback(void* context, uint32_t index) { | ||||||
|     furi_assert(message_queue); |     furi_assert(message_queue); | ||||||
|     NfcMessage message; |     NfcMessage message; | ||||||
|     if(index == 0) { |     message.type = index; | ||||||
|         message.type = NfcMessageTypeDetect; |  | ||||||
|     } else if(index == 1) { |  | ||||||
|         message.type = NfcMessageTypeReadEMV; |  | ||||||
|     } else if(index == 2) { |  | ||||||
|         message.type = NfcMessageTypeEmulateEMV; |  | ||||||
|     } else if(index == 3) { |  | ||||||
|         message.type = NfcMessageTypeEmulate; |  | ||||||
|     } else if(index == 4) { |  | ||||||
|         message.type = NfcMessageTypeField; |  | ||||||
|     } |  | ||||||
|     furi_check(osMessageQueuePut(message_queue, &message, 0, osWaitForever) == osOK); |     furi_check(osMessageQueuePut(message_queue, &message, 0, osWaitForever) == osOK); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -52,11 +42,15 @@ Nfc* nfc_alloc() { | |||||||
| 
 | 
 | ||||||
|     // Menu
 |     // Menu
 | ||||||
|     nfc->submenu = submenu_alloc(); |     nfc->submenu = submenu_alloc(); | ||||||
|     submenu_add_item(nfc->submenu, "Detect", 0, nfc_menu_callback, nfc); |     submenu_add_item(nfc->submenu, "Detect", NfcMessageTypeDetect, nfc_menu_callback, nfc); | ||||||
|     submenu_add_item(nfc->submenu, "Read EMV", 1, nfc_menu_callback, nfc); |     submenu_add_item(nfc->submenu, "Read EMV", NfcMessageTypeReadEMV, nfc_menu_callback, nfc); | ||||||
|     submenu_add_item(nfc->submenu, "Emulate EMV", 2, nfc_menu_callback, nfc); |     submenu_add_item( | ||||||
|     submenu_add_item(nfc->submenu, "Emulate", 3, nfc_menu_callback, nfc); |         nfc->submenu, "Emulate EMV", NfcMessageTypeEmulateEMV, nfc_menu_callback, nfc); | ||||||
|     submenu_add_item(nfc->submenu, "Field", 4, nfc_menu_callback, nfc); |     submenu_add_item(nfc->submenu, "Emulate", NfcMessageTypeEmulate, nfc_menu_callback, nfc); | ||||||
|  |     submenu_add_item(nfc->submenu, "Field", NfcMessageTypeField, nfc_menu_callback, nfc); | ||||||
|  |     submenu_add_item( | ||||||
|  |         nfc->submenu, "Read MfUltralight", NfcMessageTypeReadMfUltralight, nfc_menu_callback, nfc); | ||||||
|  | 
 | ||||||
|     View* submenu_view = submenu_get_view(nfc->submenu); |     View* submenu_view = submenu_get_view(nfc->submenu); | ||||||
|     view_set_previous_callback(submenu_view, nfc_view_exit); |     view_set_previous_callback(submenu_view, nfc_view_exit); | ||||||
|     view_dispatcher_add_view(nfc->view_dispatcher, NfcViewMenu, submenu_view); |     view_dispatcher_add_view(nfc->view_dispatcher, NfcViewMenu, submenu_view); | ||||||
| @ -98,6 +92,16 @@ Nfc* nfc_alloc() { | |||||||
|     view_set_previous_callback(nfc->view_field, nfc_view_stop); |     view_set_previous_callback(nfc->view_field, nfc_view_stop); | ||||||
|     view_dispatcher_add_view(nfc->view_dispatcher, NfcViewField, nfc->view_field); |     view_dispatcher_add_view(nfc->view_dispatcher, NfcViewField, nfc->view_field); | ||||||
| 
 | 
 | ||||||
|  |     // Read Mifare Ultralight
 | ||||||
|  |     nfc->view_read_mf_ultralight = view_alloc(); | ||||||
|  |     view_set_context(nfc->view_read_mf_ultralight, nfc); | ||||||
|  |     view_set_draw_callback(nfc->view_read_mf_ultralight, nfc_view_read_mf_ultralight_draw); | ||||||
|  |     view_set_previous_callback(nfc->view_read_mf_ultralight, nfc_view_stop); | ||||||
|  |     view_allocate_model( | ||||||
|  |         nfc->view_read_mf_ultralight, ViewModelTypeLocking, sizeof(NfcViewReadModel)); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         nfc->view_dispatcher, NfcViewReadMfUltralight, nfc->view_read_mf_ultralight); | ||||||
|  | 
 | ||||||
|     // Error
 |     // Error
 | ||||||
|     nfc->view_error = view_alloc(); |     nfc->view_error = view_alloc(); | ||||||
|     view_set_context(nfc->view_error, nfc); |     view_set_context(nfc->view_error, nfc); | ||||||
| @ -144,6 +148,10 @@ void nfc_free(Nfc* nfc) { | |||||||
|     view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewField); |     view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewField); | ||||||
|     view_free(nfc->view_field); |     view_free(nfc->view_field); | ||||||
| 
 | 
 | ||||||
|  |     // Read Mifare Ultralight
 | ||||||
|  |     view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewReadMfUltralight); | ||||||
|  |     view_free(nfc->view_read_mf_ultralight); | ||||||
|  | 
 | ||||||
|     // Error
 |     // Error
 | ||||||
|     view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewError); |     view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewError); | ||||||
|     view_free(nfc->view_error); |     view_free(nfc->view_error); | ||||||
| @ -180,6 +188,7 @@ int32_t nfc_task(void* p) { | |||||||
|     NfcMessage message; |     NfcMessage message; | ||||||
|     while(1) { |     while(1) { | ||||||
|         furi_check(osMessageQueueGet(message_queue, &message, NULL, osWaitForever) == osOK); |         furi_check(osMessageQueueGet(message_queue, &message, NULL, osWaitForever) == osOK); | ||||||
|  | 
 | ||||||
|         if(message.type == NfcMessageTypeDetect) { |         if(message.type == NfcMessageTypeDetect) { | ||||||
|             with_view_model( |             with_view_model( | ||||||
|                 nfc->view_detect, (NfcViewReadModel * model) { |                 nfc->view_detect, (NfcViewReadModel * model) { | ||||||
| @ -200,6 +209,8 @@ int32_t nfc_task(void* p) { | |||||||
|             nfc_start(nfc, NfcViewEmulate, NfcWorkerStateEmulate); |             nfc_start(nfc, NfcViewEmulate, NfcWorkerStateEmulate); | ||||||
|         } else if(message.type == NfcMessageTypeField) { |         } else if(message.type == NfcMessageTypeField) { | ||||||
|             nfc_start(nfc, NfcViewField, NfcWorkerStateField); |             nfc_start(nfc, NfcViewField, NfcWorkerStateField); | ||||||
|  |         } else if(message.type == NfcMessageTypeReadMfUltralight) { | ||||||
|  |             nfc_start(nfc, NfcViewReadMfUltralight, NfcWorkerStateReadMfUltralight); | ||||||
|         } else if(message.type == NfcMessageTypeStop) { |         } else if(message.type == NfcMessageTypeStop) { | ||||||
|             nfc_worker_stop(nfc->worker); |             nfc_worker_stop(nfc->worker); | ||||||
|         } else if(message.type == NfcMessageTypeDeviceFound) { |         } else if(message.type == NfcMessageTypeDeviceFound) { | ||||||
| @ -228,6 +239,19 @@ int32_t nfc_task(void* p) { | |||||||
|                     model->found = false; |                     model->found = false; | ||||||
|                     return true; |                     return true; | ||||||
|                 }); |                 }); | ||||||
|  |         } else if(message.type == NfcMessageTypeMfUlFound) { | ||||||
|  |             with_view_model( | ||||||
|  |                 nfc->view_read_mf_ultralight, (NfcViewReadModel * model) { | ||||||
|  |                     model->found = true; | ||||||
|  |                     model->device = message.device; | ||||||
|  |                     return true; | ||||||
|  |                 }); | ||||||
|  |         } else if(message.type == NfcMessageTypeMfUlNotFound) { | ||||||
|  |             with_view_model( | ||||||
|  |                 nfc->view_read_mf_ultralight, (NfcViewReadModel * model) { | ||||||
|  |                     model->found = false; | ||||||
|  |                     return true; | ||||||
|  |                 }); | ||||||
|         } else if(message.type == NfcMessageTypeExit) { |         } else if(message.type == NfcMessageTypeExit) { | ||||||
|             nfc_free(nfc); |             nfc_free(nfc); | ||||||
|             break; |             break; | ||||||
|  | |||||||
| @ -31,6 +31,7 @@ struct Nfc { | |||||||
|     View* view_emulate_emv; |     View* view_emulate_emv; | ||||||
|     View* view_emulate; |     View* view_emulate; | ||||||
|     View* view_field; |     View* view_field; | ||||||
|  |     View* view_read_mf_ultralight; | ||||||
|     View* view_cli; |     View* view_cli; | ||||||
|     View* view_error; |     View* view_error; | ||||||
|     ViewDispatcher* view_dispatcher; |     ViewDispatcher* view_dispatcher; | ||||||
|  | |||||||
| @ -44,8 +44,8 @@ typedef enum { | |||||||
|     NfcDeviceTypeNfcb, |     NfcDeviceTypeNfcb, | ||||||
|     NfcDeviceTypeNfcf, |     NfcDeviceTypeNfcf, | ||||||
|     NfcDeviceTypeNfcv, |     NfcDeviceTypeNfcv, | ||||||
|     NfcDeviceTypeNfcMifare, |  | ||||||
|     NfcDeviceTypeEMV, |     NfcDeviceTypeEMV, | ||||||
|  |     NfcDeviceTypeMfUltralight, | ||||||
| } NfcDeviceType; | } NfcDeviceType; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
| @ -53,6 +53,12 @@ typedef struct { | |||||||
|     uint8_t number[8]; |     uint8_t number[8]; | ||||||
| } EMVCard; | } EMVCard; | ||||||
| 
 | 
 | ||||||
|  | typedef struct { | ||||||
|  |     uint8_t uid[7]; | ||||||
|  |     uint8_t man_block[12]; | ||||||
|  |     uint8_t otp[4]; | ||||||
|  | } MfUlCard; | ||||||
|  | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     NfcDeviceType type; |     NfcDeviceType type; | ||||||
|     union { |     union { | ||||||
| @ -61,6 +67,7 @@ typedef struct { | |||||||
|         rfalNfcfListenDevice nfcf; |         rfalNfcfListenDevice nfcf; | ||||||
|         rfalNfcvListenDevice nfcv; |         rfalNfcvListenDevice nfcv; | ||||||
|         EMVCard emv_card; |         EMVCard emv_card; | ||||||
|  |         MfUlCard mf_ul_card; | ||||||
|     }; |     }; | ||||||
| } NfcDevice; | } NfcDevice; | ||||||
| 
 | 
 | ||||||
| @ -75,16 +82,19 @@ typedef enum { | |||||||
|     NfcWorkerStateEmulateEMV, |     NfcWorkerStateEmulateEMV, | ||||||
|     NfcWorkerStateEmulate, |     NfcWorkerStateEmulate, | ||||||
|     NfcWorkerStateField, |     NfcWorkerStateField, | ||||||
|  |     NfcWorkerStateReadMfUltralight, | ||||||
|     // Transition
 |     // Transition
 | ||||||
|     NfcWorkerStateStop, |     NfcWorkerStateStop, | ||||||
| } NfcWorkerState; | } NfcWorkerState; | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|  |     // From Menu
 | ||||||
|     NfcMessageTypeDetect, |     NfcMessageTypeDetect, | ||||||
|     NfcMessageTypeReadEMV, |     NfcMessageTypeReadEMV, | ||||||
|     NfcMessageTypeEmulateEMV, |     NfcMessageTypeEmulateEMV, | ||||||
|     NfcMessageTypeEmulate, |     NfcMessageTypeEmulate, | ||||||
|     NfcMessageTypeField, |     NfcMessageTypeField, | ||||||
|  |     NfcMessageTypeReadMfUltralight, | ||||||
|     NfcMessageTypeStop, |     NfcMessageTypeStop, | ||||||
|     NfcMessageTypeExit, |     NfcMessageTypeExit, | ||||||
|     // From Worker
 |     // From Worker
 | ||||||
| @ -92,6 +102,8 @@ typedef enum { | |||||||
|     NfcMessageTypeDeviceNotFound, |     NfcMessageTypeDeviceNotFound, | ||||||
|     NfcMessageTypeEMVFound, |     NfcMessageTypeEMVFound, | ||||||
|     NfcMessageTypeEMVNotFound, |     NfcMessageTypeEMVNotFound, | ||||||
|  |     NfcMessageTypeMfUlFound, | ||||||
|  |     NfcMessageTypeMfUlNotFound, | ||||||
| } NfcMessageType; | } NfcMessageType; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|  | |||||||
							
								
								
									
										53
									
								
								applications/nfc/nfc_views.c
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										53
									
								
								applications/nfc/nfc_views.c
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @ -150,6 +150,59 @@ void nfc_view_emulate_draw(Canvas* canvas, void* model) { | |||||||
|     canvas_draw_str(canvas, 2, 52, "SAK: 20 ATQA: 00/04"); |     canvas_draw_str(canvas, 2, 52, "SAK: 20 ATQA: 00/04"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void nfc_view_read_mf_ultralight_draw(Canvas* canvas, void* model) { | ||||||
|  |     NfcViewReadModel* m = model; | ||||||
|  |     canvas_clear(canvas); | ||||||
|  |     canvas_set_font(canvas, FontPrimary); | ||||||
|  |     char buffer[32]; | ||||||
|  | 
 | ||||||
|  |     if(m->found) { | ||||||
|  |         canvas_draw_str(canvas, 0, 12, "Found Mifare Ultralight"); | ||||||
|  |         canvas_set_font(canvas, FontSecondary); | ||||||
|  |         canvas_draw_str(canvas, 2, 22, "UID:"); | ||||||
|  |         for(uint8_t i = 0; i < sizeof(m->device.mf_ul_card.uid); i++) { | ||||||
|  |             snprintf( | ||||||
|  |                 buffer + (i * 2), sizeof(buffer) - (i * 2), "%02X", m->device.mf_ul_card.uid[i]); | ||||||
|  |         } | ||||||
|  |         buffer[sizeof(m->device.mf_ul_card.uid) * 2] = 0; | ||||||
|  |         canvas_draw_str(canvas, 18, 22, buffer); | ||||||
|  | 
 | ||||||
|  |         uint8_t man_bl_size = sizeof(m->device.mf_ul_card.man_block); | ||||||
|  |         canvas_draw_str(canvas, 2, 32, "Manufacturer block:"); | ||||||
|  |         for(uint8_t i = 0; i < man_bl_size / 2; i++) { | ||||||
|  |             snprintf( | ||||||
|  |                 buffer + (i * 2), | ||||||
|  |                 sizeof(buffer) - (i * 2), | ||||||
|  |                 "%02X", | ||||||
|  |                 m->device.mf_ul_card.man_block[i]); | ||||||
|  |         } | ||||||
|  |         buffer[man_bl_size] = 0; | ||||||
|  |         canvas_draw_str(canvas, 2, 42, buffer); | ||||||
|  | 
 | ||||||
|  |         for(uint8_t i = 0; i < man_bl_size / 2; i++) { | ||||||
|  |             snprintf( | ||||||
|  |                 buffer + (i * 2), | ||||||
|  |                 sizeof(buffer) - (i * 2), | ||||||
|  |                 "%02X", | ||||||
|  |                 m->device.mf_ul_card.man_block[man_bl_size / 2 + i]); | ||||||
|  |         } | ||||||
|  |         buffer[man_bl_size] = 0; | ||||||
|  |         canvas_draw_str(canvas, 2, 52, buffer); | ||||||
|  | 
 | ||||||
|  |         canvas_draw_str(canvas, 2, 62, "OTP: "); | ||||||
|  |         for(uint8_t i = 0; i < sizeof(m->device.mf_ul_card.otp); i++) { | ||||||
|  |             snprintf( | ||||||
|  |                 buffer + (i * 2), sizeof(buffer) - (i * 2), "%02X", m->device.mf_ul_card.otp[i]); | ||||||
|  |         } | ||||||
|  |         buffer[sizeof(m->device.mf_ul_card.otp) * 2] = 0; | ||||||
|  |         canvas_draw_str(canvas, 22, 62, buffer); | ||||||
|  |     } else { | ||||||
|  |         canvas_draw_str(canvas, 0, 12, "Searching"); | ||||||
|  |         canvas_set_font(canvas, FontSecondary); | ||||||
|  |         canvas_draw_str(canvas, 2, 22, "Place card to the back"); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void nfc_view_field_draw(Canvas* canvas, void* model) { | void nfc_view_field_draw(Canvas* canvas, void* model) { | ||||||
|     canvas_clear(canvas); |     canvas_clear(canvas); | ||||||
|     canvas_set_font(canvas, FontPrimary); |     canvas_set_font(canvas, FontPrimary); | ||||||
|  | |||||||
| @ -14,6 +14,7 @@ typedef enum { | |||||||
|     NfcViewEmulateEMV, |     NfcViewEmulateEMV, | ||||||
|     NfcViewEmulate, |     NfcViewEmulate, | ||||||
|     NfcViewField, |     NfcViewField, | ||||||
|  |     NfcViewReadMfUltralight, | ||||||
|     NfcViewError, |     NfcViewError, | ||||||
| } NfcView; | } NfcView; | ||||||
| 
 | 
 | ||||||
| @ -31,6 +32,7 @@ void nfc_view_read_emv_draw(Canvas* canvas, void* model); | |||||||
| 
 | 
 | ||||||
| void nfc_view_emulate_emv_draw(Canvas* canvas, void* model); | void nfc_view_emulate_emv_draw(Canvas* canvas, void* model); | ||||||
| void nfc_view_emulate_draw(Canvas* canvas, void* model); | void nfc_view_emulate_draw(Canvas* canvas, void* model); | ||||||
|  | void nfc_view_read_mf_ultralight_draw(Canvas* canvas, void* model); | ||||||
| 
 | 
 | ||||||
| void nfc_view_field_draw(Canvas* canvas, void* model); | void nfc_view_field_draw(Canvas* canvas, void* model); | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										123
									
								
								applications/nfc/nfc_worker.c
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										123
									
								
								applications/nfc/nfc_worker.c
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @ -1,6 +1,7 @@ | |||||||
| #include "nfc_worker_i.h" | #include "nfc_worker_i.h" | ||||||
| #include <api-hal.h> | #include <api-hal.h> | ||||||
| #include "nfc_protocols/emv_decoder.h" | #include "nfc_protocols/emv_decoder.h" | ||||||
|  | #include "nfc_protocols/mifare_ultralight.h" | ||||||
| 
 | 
 | ||||||
| #define NFC_WORKER_TAG "nfc worker" | #define NFC_WORKER_TAG "nfc worker" | ||||||
| 
 | 
 | ||||||
| @ -71,6 +72,8 @@ void nfc_worker_task(void* context) { | |||||||
|         nfc_worker_emulate(nfc_worker); |         nfc_worker_emulate(nfc_worker); | ||||||
|     } else if(nfc_worker->state == NfcWorkerStateField) { |     } else if(nfc_worker->state == NfcWorkerStateField) { | ||||||
|         nfc_worker_field(nfc_worker); |         nfc_worker_field(nfc_worker); | ||||||
|  |     } else if(nfc_worker->state == NfcWorkerStateReadMfUltralight) { | ||||||
|  |         nfc_worker_read_mf_ultralight(nfc_worker); | ||||||
|     } |     } | ||||||
|     api_hal_nfc_deactivate(); |     api_hal_nfc_deactivate(); | ||||||
|     nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); |     nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); | ||||||
| @ -306,6 +309,126 @@ void nfc_worker_poll(NfcWorker* nfc_worker) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker) { | ||||||
|  |     ReturnCode err; | ||||||
|  |     rfalNfcDevice* dev_list; | ||||||
|  |     uint8_t dev_cnt = 0; | ||||||
|  |     uint8_t tx_buff[255] = {}; | ||||||
|  |     uint16_t tx_len = 0; | ||||||
|  |     uint8_t* rx_buff; | ||||||
|  |     uint16_t* rx_len; | ||||||
|  |     MfUltralightRead mf_ul_read; | ||||||
|  | 
 | ||||||
|  |     // Update screen before start searching
 | ||||||
|  |     NfcMessage message = {.type = NfcMessageTypeMfUlNotFound}; | ||||||
|  |     while(nfc_worker->state == NfcWorkerStateReadMfUltralight) { | ||||||
|  |         furi_check( | ||||||
|  |             osMessageQueuePut(nfc_worker->message_queue, &message, 0, osWaitForever) == osOK); | ||||||
|  |         api_hal_nfc_deactivate(); | ||||||
|  |         memset(&mf_ul_read, 0, sizeof(mf_ul_read)); | ||||||
|  |         if(api_hal_nfc_detect(&dev_list, &dev_cnt, 100, false)) { | ||||||
|  |             if(dev_list[0].type == RFAL_NFC_LISTEN_TYPE_NFCA && | ||||||
|  |                mf_ul_check_card_type( | ||||||
|  |                    dev_list[0].dev.nfca.sensRes.anticollisionInfo, | ||||||
|  |                    dev_list[0].dev.nfca.sensRes.platformInfo, | ||||||
|  |                    dev_list[0].dev.nfca.selRes.sak)) { | ||||||
|  |                 // Get Mifare Ultralight version
 | ||||||
|  |                 FURI_LOG_I( | ||||||
|  |                     NFC_WORKER_TAG, "Found Mifare Ultralight tag. Trying to get tag version"); | ||||||
|  |                 tx_len = mf_ul_prepare_get_version(tx_buff); | ||||||
|  |                 err = api_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false); | ||||||
|  |                 if(err == ERR_NONE) { | ||||||
|  |                     mf_ul_parse_get_version_response(rx_buff, &mf_ul_read); | ||||||
|  |                     FURI_LOG_I( | ||||||
|  |                         NFC_WORKER_TAG, | ||||||
|  |                         "Mifare Ultralight Type: %d, Pages: %d", | ||||||
|  |                         mf_ul_read.type, | ||||||
|  |                         mf_ul_read.pages_to_read); | ||||||
|  |                 } else if(err == ERR_TIMEOUT) { | ||||||
|  |                     FURI_LOG_W( | ||||||
|  |                         NFC_WORKER_TAG, | ||||||
|  |                         "Card doesn't respond to GET VERSION command. Reinit card and set default read parameters"); | ||||||
|  |                     err = ERR_NONE; | ||||||
|  |                     mf_ul_set_default_version(&mf_ul_read); | ||||||
|  |                     // Reinit device
 | ||||||
|  |                     api_hal_nfc_deactivate(); | ||||||
|  |                     if(!api_hal_nfc_detect(&dev_list, &dev_cnt, 100, false)) { | ||||||
|  |                         FURI_LOG_E(NFC_WORKER_TAG, "Lost connection. Restarting search"); | ||||||
|  |                         message.type = NfcMessageTypeMfUlNotFound; | ||||||
|  |                         continue; | ||||||
|  |                     } | ||||||
|  |                 } else { | ||||||
|  |                     FURI_LOG_E( | ||||||
|  |                         NFC_WORKER_TAG, | ||||||
|  |                         "Error getting Mifare Ultralight version. Error code: %d", | ||||||
|  |                         err); | ||||||
|  |                     message.type = NfcMessageTypeMfUlNotFound; | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 // Dump Mifare Ultralight card
 | ||||||
|  |                 FURI_LOG_I(NFC_WORKER_TAG, "Trying to read pages"); | ||||||
|  |                 if(mf_ul_read.support_fast_read) { | ||||||
|  |                     // Read card with FAST_READ command
 | ||||||
|  |                     tx_len = mf_ul_prepare_fast_read(tx_buff, 0x00, mf_ul_read.pages_to_read - 1); | ||||||
|  |                     err = api_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false); | ||||||
|  |                     if(err == ERR_NONE) { | ||||||
|  |                         FURI_LOG_I( | ||||||
|  |                             NFC_WORKER_TAG, | ||||||
|  |                             "Fast read pages %d - %d succeed", | ||||||
|  |                             0, | ||||||
|  |                             mf_ul_read.pages_to_read - 1); | ||||||
|  |                         memcpy(mf_ul_read.dump, rx_buff, mf_ul_read.pages_to_read * 4); | ||||||
|  |                         mf_ul_read.pages_readed = mf_ul_read.pages_to_read; | ||||||
|  |                     } else { | ||||||
|  |                         FURI_LOG_E(NFC_WORKER_TAG, "Fast read failed"); | ||||||
|  |                         message.type = NfcMessageTypeMfUlNotFound; | ||||||
|  |                         continue; | ||||||
|  |                     } | ||||||
|  |                 } else { | ||||||
|  |                     // READ card with READ command (4 pages at a time)
 | ||||||
|  |                     for(uint8_t page = 0; page < mf_ul_read.pages_to_read; page += 4) { | ||||||
|  |                         tx_len = mf_ul_prepare_read(tx_buff, page); | ||||||
|  |                         err = api_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false); | ||||||
|  |                         if(err == ERR_NONE) { | ||||||
|  |                             FURI_LOG_I( | ||||||
|  |                                 NFC_WORKER_TAG, "Read pages %d - %d succeed", page, page + 3); | ||||||
|  |                             memcpy(&mf_ul_read.dump[page * 4], rx_buff, 4 * 4); | ||||||
|  |                             mf_ul_read.pages_readed += 4; | ||||||
|  |                         } else { | ||||||
|  |                             FURI_LOG_W( | ||||||
|  |                                 NFC_WORKER_TAG, "Read pages %d - %d failed", page, page + 3); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 // Fill message for nfc application
 | ||||||
|  |                 message.type = NfcMessageTypeMfUlFound; | ||||||
|  |                 memcpy( | ||||||
|  |                     message.device.mf_ul_card.uid, | ||||||
|  |                     dev_list[0].dev.nfca.nfcId1, | ||||||
|  |                     sizeof(message.device.mf_ul_card.uid)); | ||||||
|  |                 memcpy(message.device.mf_ul_card.man_block, mf_ul_read.dump, 4 * 3); | ||||||
|  |                 memcpy(message.device.mf_ul_card.otp, &mf_ul_read.dump[4 * 3], 4); | ||||||
|  |                 for(uint8_t i = 0; i < mf_ul_read.pages_readed * 4; i += 4) { | ||||||
|  |                     printf("Page %2d: ", i / 4); | ||||||
|  |                     for(uint8_t j = 0; j < 4; j++) { | ||||||
|  |                         printf("%02X ", mf_ul_read.dump[i + j]); | ||||||
|  |                     } | ||||||
|  |                     printf("\r\n"); | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 message.type = NfcMessageTypeMfUlNotFound; | ||||||
|  |                 FURI_LOG_W(NFC_WORKER_TAG, "Tag does not support Mifare Ultralight"); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             message.type = NfcMessageTypeMfUlNotFound; | ||||||
|  |             FURI_LOG_W(NFC_WORKER_TAG, "Can't find any tags"); | ||||||
|  |         } | ||||||
|  |         osDelay(100); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void nfc_worker_emulate(NfcWorker* nfc_worker) { | void nfc_worker_emulate(NfcWorker* nfc_worker) { | ||||||
|     while(nfc_worker->state == NfcWorkerStateEmulate) { |     while(nfc_worker->state == NfcWorkerStateEmulate) { | ||||||
|         if(api_hal_nfc_listen(100)) { |         if(api_hal_nfc_listen(100)) { | ||||||
|  | |||||||
| @ -38,3 +38,5 @@ void nfc_worker_poll(NfcWorker* nfc_worker); | |||||||
| void nfc_worker_emulate(NfcWorker* nfc_worker); | void nfc_worker_emulate(NfcWorker* nfc_worker); | ||||||
| 
 | 
 | ||||||
| void nfc_worker_field(NfcWorker* nfc_worker); | void nfc_worker_field(NfcWorker* nfc_worker); | ||||||
|  | 
 | ||||||
|  | void nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker); | ||||||
|  | |||||||
| @ -1,3 +1,5 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  | |||||||
							
								
								
									
										59
									
								
								lib/nfc_protocols/mifare_ultralight.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								lib/nfc_protocols/mifare_ultralight.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,59 @@ | |||||||
|  | #include "mifare_ultralight.h" | ||||||
|  | 
 | ||||||
|  | bool mf_ul_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { | ||||||
|  |     if((ATQA0 == 0x44) && (ATQA1 == 0x00) && (SAK == 0x00)) { | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint16_t mf_ul_prepare_get_version(uint8_t* dest) { | ||||||
|  |     dest[0] = MF_UL_GET_VERSION_CMD; | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void mf_ul_parse_get_version_response(uint8_t* buff, MfUltralightRead* mf_ul_read) { | ||||||
|  |     MfUltralightVersion* version = (MfUltralightVersion*) buff; | ||||||
|  |     if(version->storage_size == 0x0B || version->storage_size == 0x00) { | ||||||
|  |         mf_ul_read->type = MfUltralightTypeUL11; | ||||||
|  |         mf_ul_read->pages_to_read = 20; | ||||||
|  |         mf_ul_read->support_fast_read = true; | ||||||
|  |     } else if(version->storage_size == 0x0E) { | ||||||
|  |         mf_ul_read->type = MfUltralightTypeUL21; | ||||||
|  |         mf_ul_read->pages_to_read = 41; | ||||||
|  |         mf_ul_read->support_fast_read = true; | ||||||
|  |     } else if(version->storage_size == 0x0F) { | ||||||
|  |         mf_ul_read->type = MfUltralightTypeNTAG213; | ||||||
|  |         mf_ul_read->pages_to_read = 45; | ||||||
|  |         mf_ul_read->support_fast_read = false; | ||||||
|  |     } else if(version->storage_size == 0x11) { | ||||||
|  |         mf_ul_read->type = MfUltralightTypeNTAG215; | ||||||
|  |         mf_ul_read->pages_to_read = 135; | ||||||
|  |         mf_ul_read->support_fast_read = false; | ||||||
|  |     } else if(version->storage_size == 0x13) { | ||||||
|  |         mf_ul_read->type = MfUltralightTypeNTAG216; | ||||||
|  |         mf_ul_read->pages_to_read = 231; | ||||||
|  |         mf_ul_read->support_fast_read = false; | ||||||
|  |     } else { | ||||||
|  |         mf_ul_set_default_version(mf_ul_read); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void mf_ul_set_default_version(MfUltralightRead* mf_ul_read) { | ||||||
|  |     mf_ul_read->type = MfUltralightTypeUnknown; | ||||||
|  |     mf_ul_read->pages_to_read = 20; | ||||||
|  |     mf_ul_read->support_fast_read = false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint16_t mf_ul_prepare_read(uint8_t* dest, uint8_t start_page) { | ||||||
|  |     dest[0] = MF_UL_READ_CMD; | ||||||
|  |     dest[1] = start_page; | ||||||
|  |     return 2; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint16_t mf_ul_prepare_fast_read(uint8_t* dest, uint8_t start_page, uint8_t end_page) { | ||||||
|  |     dest[0] = MF_UL_FAST_READ_CMD; | ||||||
|  |     dest[1] = start_page; | ||||||
|  |     dest[2] = end_page; | ||||||
|  |     return 3; | ||||||
|  | } | ||||||
							
								
								
									
										56
									
								
								lib/nfc_protocols/mifare_ultralight.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								lib/nfc_protocols/mifare_ultralight.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,56 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | #define MF_UL_GET_VERSION_CMD (0x60) | ||||||
|  | #define MF_UL_READ_CMD (0x30) | ||||||
|  | #define MF_UL_FAST_READ_CMD (0x3A) | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     MfUltralightTypeUnknown, | ||||||
|  |     MfUltralightTypeUL11, | ||||||
|  |     MfUltralightTypeUL21, | ||||||
|  |     MfUltralightTypeNTAG213, | ||||||
|  |     MfUltralightTypeNTAG215, | ||||||
|  |     MfUltralightTypeNTAG216, | ||||||
|  | } MfUltralightType; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     uint8_t header; | ||||||
|  |     uint8_t vendor_id; | ||||||
|  |     uint8_t prod_type; | ||||||
|  |     uint8_t prod_subtype; | ||||||
|  |     uint8_t prod_ver_major; | ||||||
|  |     uint8_t prod_ver_minor; | ||||||
|  |     uint8_t storage_size; | ||||||
|  |     uint8_t protocol_type; | ||||||
|  | } MfUltralightVersion; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     uint8_t  sn0[3]; | ||||||
|  |     uint8_t  btBCC0; | ||||||
|  |     uint8_t  sn1[4]; | ||||||
|  |     uint8_t  btBCC1; | ||||||
|  |     uint8_t  internal; | ||||||
|  |     uint8_t  lock[2]; | ||||||
|  |     uint8_t  otp[4]; | ||||||
|  | } MfUltralightManufacturerBlock; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     MfUltralightType type; | ||||||
|  |     uint8_t pages_to_read; | ||||||
|  |     uint8_t pages_readed; | ||||||
|  |     bool support_fast_read; | ||||||
|  |     uint8_t dump[255]; | ||||||
|  | } MfUltralightRead; | ||||||
|  | 
 | ||||||
|  | bool mf_ul_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK); | ||||||
|  | 
 | ||||||
|  | uint16_t mf_ul_prepare_get_version(uint8_t* dest); | ||||||
|  | void mf_ul_parse_get_version_response(uint8_t* buff, MfUltralightRead* mf_ul_read); | ||||||
|  | void mf_ul_set_default_version(MfUltralightRead* mf_ul_read); | ||||||
|  | 
 | ||||||
|  | uint16_t mf_ul_prepare_read(uint8_t* dest, uint8_t start_page); | ||||||
|  | uint16_t mf_ul_prepare_fast_read(uint8_t* dest, uint8_t start_page, uint8_t end_page); | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 gornekich
						gornekich