[FL-1526] Mifare Ultralight emulation (#645)
* rfal: add discovery parameter for passing listen activation * nfc: add discovery parameter to furi_hal_nfc_listen * mifare_ul: add emulation parsing commands * nfc: add mifare ul emulation * nfc: switch to mifare ultralight emulation form menu * furi-hal-nfc: add first frame reception in emulation mode * nfc: change argument check * nfc: rework nfc worker and mifare ul lib * mifare_ul: add write and cnt increment commands * nfc: add card modification check * mifare_ul: add data changed flag * nfc: add shodow files * nfc: add restore original file Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									e1d80d5402
								
							
						
					
					
						commit
						c122317468
					
				@ -151,9 +151,10 @@ void nfc_text_store_clear(Nfc* nfc) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
int32_t nfc_app(void* p) {
 | 
					int32_t nfc_app(void* p) {
 | 
				
			||||||
    Nfc* nfc = nfc_alloc();
 | 
					    Nfc* nfc = nfc_alloc();
 | 
				
			||||||
 | 
					    char* args = p;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Check argument and run corresponding scene
 | 
					    // Check argument and run corresponding scene
 | 
				
			||||||
    if(p && nfc_device_load(&nfc->dev, p)) {
 | 
					    if((*args != '\0') && nfc_device_load(&nfc->dev, p)) {
 | 
				
			||||||
        scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
 | 
					        scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        scene_manager_next_scene(nfc->scene_manager, NfcSceneStart);
 | 
					        scene_manager_next_scene(nfc->scene_manager, NfcSceneStart);
 | 
				
			||||||
 | 
				
			|||||||
@ -64,7 +64,7 @@ void nfc_cli_emulate(Cli* cli, string_t args, void* context) {
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    while(!cli_cmd_interrupt_received(cli)) {
 | 
					    while(!cli_cmd_interrupt_received(cli)) {
 | 
				
			||||||
        if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, 100)) {
 | 
					        if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 100)) {
 | 
				
			||||||
            printf("Reader detected\r\n");
 | 
					            printf("Reader detected\r\n");
 | 
				
			||||||
            furi_hal_nfc_deactivate();
 | 
					            furi_hal_nfc_deactivate();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -8,6 +8,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
static const char* nfc_app_folder = "/any/nfc";
 | 
					static const char* nfc_app_folder = "/any/nfc";
 | 
				
			||||||
static const char* nfc_app_extension = ".nfc";
 | 
					static const char* nfc_app_extension = ".nfc";
 | 
				
			||||||
 | 
					static const char* nfc_app_shadow_extension = ".shd";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool nfc_device_read_hex(string_t str, uint8_t* buff, uint16_t len) {
 | 
					static bool nfc_device_read_hex(string_t str, uint8_t* buff, uint16_t len) {
 | 
				
			||||||
    string_strim(str);
 | 
					    string_strim(str);
 | 
				
			||||||
@ -259,7 +260,11 @@ void nfc_device_set_name(NfcDevice* dev, const char* name) {
 | 
				
			|||||||
    strlcpy(dev->dev_name, name, NFC_DEV_NAME_MAX_LEN);
 | 
					    strlcpy(dev->dev_name, name, NFC_DEV_NAME_MAX_LEN);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool nfc_device_save(NfcDevice* dev, const char* dev_name) {
 | 
					static bool nfc_device_save_file(
 | 
				
			||||||
 | 
					    NfcDevice* dev,
 | 
				
			||||||
 | 
					    const char* dev_name,
 | 
				
			||||||
 | 
					    const char* folder,
 | 
				
			||||||
 | 
					    const char* extension) {
 | 
				
			||||||
    furi_assert(dev);
 | 
					    furi_assert(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    FileWorker* file_worker = file_worker_alloc(false);
 | 
					    FileWorker* file_worker = file_worker_alloc(false);
 | 
				
			||||||
@ -275,7 +280,7 @@ bool nfc_device_save(NfcDevice* dev, const char* dev_name) {
 | 
				
			|||||||
            break;
 | 
					            break;
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        // First remove nfc device file if it was saved
 | 
					        // First remove nfc device file if it was saved
 | 
				
			||||||
        string_printf(dev_file_name, "%s/%s%s", nfc_app_folder, dev_name, nfc_app_extension);
 | 
					        string_printf(dev_file_name, "%s/%s%s", folder, dev_name, extension);
 | 
				
			||||||
        if(!file_worker_remove(file_worker, string_get_cstr(dev_file_name))) {
 | 
					        if(!file_worker_remove(file_worker, string_get_cstr(dev_file_name))) {
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
@ -316,16 +321,42 @@ bool nfc_device_save(NfcDevice* dev, const char* dev_name) {
 | 
				
			|||||||
    return true;
 | 
					    return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool nfc_device_save(NfcDevice* dev, const char* dev_name) {
 | 
				
			||||||
 | 
					    return nfc_device_save_file(dev, dev_name, nfc_app_folder, nfc_app_extension);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool nfc_device_save_shadow(NfcDevice* dev, const char* dev_name) {
 | 
				
			||||||
 | 
					    dev->shadow_file_exist = true;
 | 
				
			||||||
 | 
					    return nfc_device_save_file(dev, dev_name, nfc_app_folder, nfc_app_shadow_extension);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool nfc_device_load_data(FileWorker* file_worker, string_t path, NfcDevice* dev) {
 | 
					static bool nfc_device_load_data(FileWorker* file_worker, string_t path, NfcDevice* dev) {
 | 
				
			||||||
    string_t temp_string;
 | 
					    string_t temp_string;
 | 
				
			||||||
    string_init(temp_string);
 | 
					    string_init(temp_string);
 | 
				
			||||||
    bool parsed = false;
 | 
					    bool parsed = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    do {
 | 
					    do {
 | 
				
			||||||
        // Open key file
 | 
					        // Check existance of shadow file
 | 
				
			||||||
        if(!file_worker_open(file_worker, string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) {
 | 
					        size_t ext_start = string_search_str(path, nfc_app_extension);
 | 
				
			||||||
 | 
					        string_set_n(temp_string, path, 0, ext_start);
 | 
				
			||||||
 | 
					        string_cat_printf(temp_string, "%s", nfc_app_shadow_extension);
 | 
				
			||||||
 | 
					        if(!file_worker_is_file_exist(
 | 
				
			||||||
 | 
					               file_worker, string_get_cstr(temp_string), &dev->shadow_file_exist)) {
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        // Open shadow file if it exists. If not - open original
 | 
				
			||||||
 | 
					        if(dev->shadow_file_exist) {
 | 
				
			||||||
 | 
					            if(!file_worker_open(
 | 
				
			||||||
 | 
					                   file_worker, string_get_cstr(temp_string), FSAM_READ, FSOM_OPEN_EXISTING)) {
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            if(!file_worker_open(
 | 
				
			||||||
 | 
					                   file_worker, string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) {
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Read and parse format from 1st line
 | 
					        // Read and parse format from 1st line
 | 
				
			||||||
        if(!file_worker_read_until(file_worker, temp_string, '\n')) {
 | 
					        if(!file_worker_read_until(file_worker, temp_string, '\n')) {
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
@ -427,13 +458,61 @@ void nfc_device_clear(NfcDevice* dev) {
 | 
				
			|||||||
bool nfc_device_delete(NfcDevice* dev) {
 | 
					bool nfc_device_delete(NfcDevice* dev) {
 | 
				
			||||||
    furi_assert(dev);
 | 
					    furi_assert(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool result = false;
 | 
					    bool result = true;
 | 
				
			||||||
    FileWorker* file_worker = file_worker_alloc(false);
 | 
					    FileWorker* file_worker = file_worker_alloc(false);
 | 
				
			||||||
    string_t file_path;
 | 
					    string_t file_path;
 | 
				
			||||||
    string_init_printf(file_path, "%s/%s%s", nfc_app_folder, dev->dev_name, nfc_app_extension);
 | 
					
 | 
				
			||||||
    result = file_worker_remove(file_worker, string_get_cstr(file_path));
 | 
					    do {
 | 
				
			||||||
 | 
					        // Delete original file
 | 
				
			||||||
 | 
					        string_init_printf(file_path, "%s/%s%s", nfc_app_folder, dev->dev_name, nfc_app_extension);
 | 
				
			||||||
 | 
					        if(!file_worker_remove(file_worker, string_get_cstr(file_path))) {
 | 
				
			||||||
 | 
					            result = false;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // Delete shadow file if it exists
 | 
				
			||||||
 | 
					        if(dev->shadow_file_exist) {
 | 
				
			||||||
 | 
					            string_clean(file_path);
 | 
				
			||||||
 | 
					            string_printf(
 | 
				
			||||||
 | 
					                file_path, "%s/%s%s", nfc_app_folder, dev->dev_name, nfc_app_shadow_extension);
 | 
				
			||||||
 | 
					            if(!file_worker_remove(file_worker, string_get_cstr(file_path))) {
 | 
				
			||||||
 | 
					                result = false;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } while(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    string_clear(file_path);
 | 
					    string_clear(file_path);
 | 
				
			||||||
    file_worker_close(file_worker);
 | 
					    file_worker_close(file_worker);
 | 
				
			||||||
    file_worker_free(file_worker);
 | 
					    file_worker_free(file_worker);
 | 
				
			||||||
    return result;
 | 
					    return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool nfc_device_restore(NfcDevice* dev) {
 | 
				
			||||||
 | 
					    furi_assert(dev);
 | 
				
			||||||
 | 
					    furi_assert(dev->shadow_file_exist);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool result = true;
 | 
				
			||||||
 | 
					    FileWorker* file_worker = file_worker_alloc(false);
 | 
				
			||||||
 | 
					    string_t path;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    do {
 | 
				
			||||||
 | 
					        string_init_printf(
 | 
				
			||||||
 | 
					            path, "%s/%s%s", nfc_app_folder, dev->dev_name, nfc_app_shadow_extension);
 | 
				
			||||||
 | 
					        if(!file_worker_remove(file_worker, string_get_cstr(path))) {
 | 
				
			||||||
 | 
					            result = false;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        dev->shadow_file_exist = false;
 | 
				
			||||||
 | 
					        string_clean(path);
 | 
				
			||||||
 | 
					        string_printf(path, "%s/%s%s", nfc_app_folder, dev->dev_name, nfc_app_extension);
 | 
				
			||||||
 | 
					        if(!nfc_device_load_data(file_worker, path, dev)) {
 | 
				
			||||||
 | 
					            result = false;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } while(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    string_clear(path);
 | 
				
			||||||
 | 
					    file_worker_close(file_worker);
 | 
				
			||||||
 | 
					    file_worker_free(file_worker);
 | 
				
			||||||
 | 
					    return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -59,12 +59,15 @@ typedef struct {
 | 
				
			|||||||
    char dev_name[NFC_DEV_NAME_MAX_LEN];
 | 
					    char dev_name[NFC_DEV_NAME_MAX_LEN];
 | 
				
			||||||
    char file_name[NFC_FILE_NAME_MAX_LEN];
 | 
					    char file_name[NFC_FILE_NAME_MAX_LEN];
 | 
				
			||||||
    NfcDeviceSaveFormat format;
 | 
					    NfcDeviceSaveFormat format;
 | 
				
			||||||
 | 
					    bool shadow_file_exist;
 | 
				
			||||||
} NfcDevice;
 | 
					} NfcDevice;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void nfc_device_set_name(NfcDevice* dev, const char* name);
 | 
					void nfc_device_set_name(NfcDevice* dev, const char* name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool nfc_device_save(NfcDevice* dev, const char* dev_name);
 | 
					bool nfc_device_save(NfcDevice* dev, const char* dev_name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool nfc_device_save_shadow(NfcDevice* dev, const char* dev_name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool nfc_device_load(NfcDevice* dev, const char* file_path);
 | 
					bool nfc_device_load(NfcDevice* dev, const char* file_path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool nfc_file_select(NfcDevice* dev);
 | 
					bool nfc_file_select(NfcDevice* dev);
 | 
				
			||||||
@ -72,3 +75,5 @@ bool nfc_file_select(NfcDevice* 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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool nfc_device_restore(NfcDevice* dev);
 | 
				
			||||||
 | 
				
			|||||||
@ -143,7 +143,7 @@ void nfc_worker_detect(NfcWorker* nfc_worker) {
 | 
				
			|||||||
void nfc_worker_emulate(NfcWorker* nfc_worker) {
 | 
					void nfc_worker_emulate(NfcWorker* nfc_worker) {
 | 
				
			||||||
    NfcDeviceCommomData* data = &nfc_worker->dev_data->nfc_data;
 | 
					    NfcDeviceCommomData* data = &nfc_worker->dev_data->nfc_data;
 | 
				
			||||||
    while(nfc_worker->state == NfcWorkerStateEmulate) {
 | 
					    while(nfc_worker->state == NfcWorkerStateEmulate) {
 | 
				
			||||||
        if(furi_hal_nfc_listen(data->uid, data->uid_len, data->atqa, data->sak, 100)) {
 | 
					        if(furi_hal_nfc_listen(data->uid, data->uid_len, data->atqa, data->sak, false, 100)) {
 | 
				
			||||||
            FURI_LOG_I(NFC_WORKER_TAG, "Reader detected");
 | 
					            FURI_LOG_I(NFC_WORKER_TAG, "Reader detected");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        osDelay(10);
 | 
					        osDelay(10);
 | 
				
			||||||
@ -406,7 +406,7 @@ void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) {
 | 
				
			|||||||
        0x00, 0x00};
 | 
					        0x00, 0x00};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    while(nfc_worker->state == NfcWorkerStateEmulateApdu) {
 | 
					    while(nfc_worker->state == NfcWorkerStateEmulateApdu) {
 | 
				
			||||||
        if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, 300)) {
 | 
					        if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 300)) {
 | 
				
			||||||
            FURI_LOG_I(NFC_WORKER_TAG, "POS terminal detected");
 | 
					            FURI_LOG_I(NFC_WORKER_TAG, "POS terminal detected");
 | 
				
			||||||
            // Read data from POS terminal
 | 
					            // Read data from POS terminal
 | 
				
			||||||
            err = furi_hal_nfc_data_exchange(NULL, 0, &rx_buff, &rx_len, false);
 | 
					            err = furi_hal_nfc_data_exchange(NULL, 0, &rx_buff, &rx_len, false);
 | 
				
			||||||
@ -608,33 +608,52 @@ void nfc_worker_emulate_mifare_ul(NfcWorker* nfc_worker) {
 | 
				
			|||||||
    uint8_t* rx_buff;
 | 
					    uint8_t* rx_buff;
 | 
				
			||||||
    uint16_t* rx_len;
 | 
					    uint16_t* rx_len;
 | 
				
			||||||
    NfcDeviceData* data = nfc_worker->dev_data;
 | 
					    NfcDeviceData* data = nfc_worker->dev_data;
 | 
				
			||||||
 | 
					    MifareUlDevice mf_ul_emulate;
 | 
				
			||||||
 | 
					    // Setup emulation parameters from mifare ultralight data structure
 | 
				
			||||||
 | 
					    mf_ul_prepare_emulation(&mf_ul_emulate, &data->mf_ul_data);
 | 
				
			||||||
    while(nfc_worker->state == NfcWorkerStateEmulateMifareUl) {
 | 
					    while(nfc_worker->state == NfcWorkerStateEmulateMifareUl) {
 | 
				
			||||||
        if(furi_hal_nfc_listen(
 | 
					        if(furi_hal_nfc_listen(
 | 
				
			||||||
               data->nfc_data.uid,
 | 
					               data->nfc_data.uid,
 | 
				
			||||||
               data->nfc_data.uid_len,
 | 
					               data->nfc_data.uid_len,
 | 
				
			||||||
               data->nfc_data.atqa,
 | 
					               data->nfc_data.atqa,
 | 
				
			||||||
               data->nfc_data.sak,
 | 
					               data->nfc_data.sak,
 | 
				
			||||||
               1000)) {
 | 
					               true,
 | 
				
			||||||
            FURI_LOG_I(NFC_WORKER_TAG, "Hello my dudes");
 | 
					               200)) {
 | 
				
			||||||
            // Prepare version answer
 | 
					            FURI_LOG_D(NFC_WORKER_TAG, "Anticollision passed");
 | 
				
			||||||
            tx_len = sizeof(data->mf_ul_data.version);
 | 
					            if(furi_hal_nfc_get_first_frame(&rx_buff, &rx_len)) {
 | 
				
			||||||
            memcpy(tx_buff, &data->mf_ul_data.version, tx_len);
 | 
					                // Data exchange loop
 | 
				
			||||||
            err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
 | 
					                while(nfc_worker->state == NfcWorkerStateEmulateMifareUl) {
 | 
				
			||||||
            if(err == ERR_NONE) {
 | 
					                    tx_len = mf_ul_prepare_emulation_response(
 | 
				
			||||||
                FURI_LOG_I(NFC_WORKER_TAG, "Received 1st message:");
 | 
					                        rx_buff, *rx_len, tx_buff, &mf_ul_emulate);
 | 
				
			||||||
                for(uint16_t i = 0; i < *rx_len; i++) {
 | 
					                    if(tx_len > 0) {
 | 
				
			||||||
                    printf("%02X ", rx_buff[i]);
 | 
					                        err =
 | 
				
			||||||
 | 
					                            furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
 | 
				
			||||||
 | 
					                        if(err == ERR_NONE) {
 | 
				
			||||||
 | 
					                            continue;
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            FURI_LOG_E(NFC_WORKER_TAG, "Communication error: %d", err);
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        FURI_LOG_W(NFC_WORKER_TAG, "Not valid command: %02X", rx_buff[0]);
 | 
				
			||||||
 | 
					                        furi_hal_nfc_deactivate();
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                printf("\r\n");
 | 
					 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                FURI_LOG_E(NFC_WORKER_TAG, "Error in 1st data exchange: select PPSE");
 | 
					                FURI_LOG_W(NFC_WORKER_TAG, "Error in 1st data exchange");
 | 
				
			||||||
                furi_hal_nfc_deactivate();
 | 
					                furi_hal_nfc_deactivate();
 | 
				
			||||||
                continue;
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        FURI_LOG_W(NFC_WORKER_TAG, "Hello my dudes");
 | 
					        // Check if data was modified
 | 
				
			||||||
        osDelay(10);
 | 
					        if(mf_ul_emulate.data_changed) {
 | 
				
			||||||
 | 
					            nfc_worker->dev_data->mf_ul_data = mf_ul_emulate.data;
 | 
				
			||||||
 | 
					            if(nfc_worker->callback) {
 | 
				
			||||||
 | 
					                nfc_worker->callback(nfc_worker->context);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        FURI_LOG_W(NFC_WORKER_TAG, "Can't find reader");
 | 
				
			||||||
 | 
					        osThreadYield();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -26,3 +26,4 @@ ADD_SCENE(nfc, run_emv_app_confirm, RunEmvAppConfirm)
 | 
				
			|||||||
ADD_SCENE(nfc, read_emv_data, ReadEmvData)
 | 
					ADD_SCENE(nfc, read_emv_data, ReadEmvData)
 | 
				
			||||||
ADD_SCENE(nfc, read_emv_data_success, ReadEmvDataSuccess)
 | 
					ADD_SCENE(nfc, read_emv_data_success, ReadEmvDataSuccess)
 | 
				
			||||||
ADD_SCENE(nfc, emulate_apdu_sequence, EmulateApduSequence)
 | 
					ADD_SCENE(nfc, emulate_apdu_sequence, EmulateApduSequence)
 | 
				
			||||||
 | 
					ADD_SCENE(nfc, restore_original, RestoreOriginal)
 | 
				
			||||||
 | 
				
			|||||||
@ -1,38 +1,33 @@
 | 
				
			|||||||
#include "../nfc_i.h"
 | 
					#include "../nfc_i.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define NFC_MF_UL_DATA_NOT_CHANGED (0UL)
 | 
				
			||||||
 | 
					#define NFC_MF_UL_DATA_CHANGED (1UL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void nfc_emulate_mifare_ul_worker_callback(void* context) {
 | 
				
			||||||
 | 
					    Nfc* nfc = (Nfc*)context;
 | 
				
			||||||
 | 
					    scene_manager_set_scene_state(
 | 
				
			||||||
 | 
					        nfc->scene_manager, NfcSceneEmulateMifareUl, NFC_MF_UL_DATA_CHANGED);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const void nfc_scene_emulate_mifare_ul_on_enter(void* context) {
 | 
					const void nfc_scene_emulate_mifare_ul_on_enter(void* context) {
 | 
				
			||||||
    Nfc* nfc = (Nfc*)context;
 | 
					    Nfc* nfc = (Nfc*)context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Setup view
 | 
					    // Setup view
 | 
				
			||||||
    Popup* popup = nfc->popup;
 | 
					    Popup* popup = nfc->popup;
 | 
				
			||||||
    NfcDeviceCommomData* data = &nfc->dev.dev_data.nfc_data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if(strcmp(nfc->dev.dev_name, "")) {
 | 
					    if(strcmp(nfc->dev.dev_name, "")) {
 | 
				
			||||||
        nfc_text_store_set(nfc, "%s", nfc->dev.dev_name);
 | 
					        nfc_text_store_set(nfc, "%s", nfc->dev.dev_name);
 | 
				
			||||||
    } else if(data->uid_len == 4) {
 | 
					 | 
				
			||||||
        nfc_text_store_set(
 | 
					 | 
				
			||||||
            nfc, "%02X %02X %02X %02X", data->uid[0], data->uid[1], data->uid[2], data->uid[3]);
 | 
					 | 
				
			||||||
    } else if(data->uid_len == 7) {
 | 
					 | 
				
			||||||
        nfc_text_store_set(
 | 
					 | 
				
			||||||
            nfc,
 | 
					 | 
				
			||||||
            "%02X %02X %02X %02X\n%02X %02X %02X",
 | 
					 | 
				
			||||||
            data->uid[0],
 | 
					 | 
				
			||||||
            data->uid[1],
 | 
					 | 
				
			||||||
            data->uid[2],
 | 
					 | 
				
			||||||
            data->uid[3],
 | 
					 | 
				
			||||||
            data->uid[4],
 | 
					 | 
				
			||||||
            data->uid[5],
 | 
					 | 
				
			||||||
            data->uid[6]);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
 | 
					    popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
 | 
				
			||||||
    popup_set_header(popup, "Emulating Mf Ul", 56, 31, AlignLeft, AlignTop);
 | 
					    popup_set_header(popup, "Emulating\nMf Ultralight", 56, 31, AlignLeft, AlignTop);
 | 
				
			||||||
    popup_set_text(popup, nfc->text_store, 56, 43, AlignLeft, AlignTop);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Setup and start worker
 | 
					    // Setup and start worker
 | 
				
			||||||
 | 
					 | 
				
			||||||
    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
 | 
					    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
 | 
				
			||||||
    nfc_worker_start(nfc->worker, NfcWorkerStateEmulateMifareUl, &nfc->dev.dev_data, NULL, nfc);
 | 
					    nfc_worker_start(
 | 
				
			||||||
 | 
					        nfc->worker,
 | 
				
			||||||
 | 
					        NfcWorkerStateEmulateMifareUl,
 | 
				
			||||||
 | 
					        &nfc->dev.dev_data,
 | 
				
			||||||
 | 
					        nfc_emulate_mifare_ul_worker_callback,
 | 
				
			||||||
 | 
					        nfc);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const bool nfc_scene_emulate_mifare_ul_on_event(void* context, SceneManagerEvent event) {
 | 
					const bool nfc_scene_emulate_mifare_ul_on_event(void* context, SceneManagerEvent event) {
 | 
				
			||||||
@ -42,6 +37,17 @@ const bool nfc_scene_emulate_mifare_ul_on_event(void* context, SceneManagerEvent
 | 
				
			|||||||
    if(event.type == SceneManagerEventTypeTick) {
 | 
					    if(event.type == SceneManagerEventTypeTick) {
 | 
				
			||||||
        notification_message(nfc->notifications, &sequence_blink_blue_10);
 | 
					        notification_message(nfc->notifications, &sequence_blink_blue_10);
 | 
				
			||||||
        consumed = true;
 | 
					        consumed = true;
 | 
				
			||||||
 | 
					    } else if(event.type == SceneManagerEventTypeBack) {
 | 
				
			||||||
 | 
					        // Stop worker
 | 
				
			||||||
 | 
					        nfc_worker_stop(nfc->worker);
 | 
				
			||||||
 | 
					        // Check if data changed and save in shadow file
 | 
				
			||||||
 | 
					        if(scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmulateMifareUl) ==
 | 
				
			||||||
 | 
					           NFC_MF_UL_DATA_CHANGED) {
 | 
				
			||||||
 | 
					            scene_manager_set_scene_state(
 | 
				
			||||||
 | 
					                nfc->scene_manager, NfcSceneEmulateMifareUl, NFC_MF_UL_DATA_NOT_CHANGED);
 | 
				
			||||||
 | 
					            nfc_device_save_shadow(&nfc->dev, nfc->dev.dev_name);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        consumed = false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return consumed;
 | 
					    return consumed;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -49,9 +55,6 @@ const bool nfc_scene_emulate_mifare_ul_on_event(void* context, SceneManagerEvent
 | 
				
			|||||||
const void nfc_scene_emulate_mifare_ul_on_exit(void* context) {
 | 
					const void nfc_scene_emulate_mifare_ul_on_exit(void* context) {
 | 
				
			||||||
    Nfc* nfc = (Nfc*)context;
 | 
					    Nfc* nfc = (Nfc*)context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Stop worker
 | 
					 | 
				
			||||||
    nfc_worker_stop(nfc->worker);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Clear view
 | 
					    // Clear view
 | 
				
			||||||
    Popup* popup = nfc->popup;
 | 
					    Popup* popup = nfc->popup;
 | 
				
			||||||
    popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
 | 
					    popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
 | 
				
			||||||
 | 
				
			|||||||
@ -38,7 +38,7 @@ const bool nfc_scene_mifare_ul_menu_on_event(void* context, SceneManagerEvent ev
 | 
				
			|||||||
        } else if(event.event == SubmenuIndexEmulate) {
 | 
					        } else if(event.event == SubmenuIndexEmulate) {
 | 
				
			||||||
            scene_manager_set_scene_state(
 | 
					            scene_manager_set_scene_state(
 | 
				
			||||||
                nfc->scene_manager, NfcSceneMifareUlMenu, SubmenuIndexEmulate);
 | 
					                nfc->scene_manager, NfcSceneMifareUlMenu, SubmenuIndexEmulate);
 | 
				
			||||||
            scene_manager_next_scene(nfc->scene_manager, NfcSceneNotImplemented);
 | 
					            scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl);
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    } else if(event.type == SceneManagerEventTypeBack) {
 | 
					    } else if(event.type == SceneManagerEventTypeBack) {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										48
									
								
								applications/nfc/scenes/nfc_scene_restore_original.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								applications/nfc/scenes/nfc_scene_restore_original.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,48 @@
 | 
				
			|||||||
 | 
					#include "../nfc_i.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define SCENE_RESTORE_ORIGINAL_CUSTOM_EVENT (0UL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void nfc_scene_restore_original_popup_callback(void* context) {
 | 
				
			||||||
 | 
					    Nfc* nfc = (Nfc*)context;
 | 
				
			||||||
 | 
					    view_dispatcher_send_custom_event(nfc->view_dispatcher, SCENE_RESTORE_ORIGINAL_CUSTOM_EVENT);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const void nfc_scene_restore_original_on_enter(void* context) {
 | 
				
			||||||
 | 
					    Nfc* nfc = (Nfc*)context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Setup view
 | 
				
			||||||
 | 
					    Popup* popup = nfc->popup;
 | 
				
			||||||
 | 
					    popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
 | 
				
			||||||
 | 
					    popup_set_header(popup, "Original file\nrestored", 13, 22, AlignLeft, AlignBottom);
 | 
				
			||||||
 | 
					    popup_set_timeout(popup, 1500);
 | 
				
			||||||
 | 
					    popup_set_context(popup, nfc);
 | 
				
			||||||
 | 
					    popup_set_callback(popup, nfc_scene_restore_original_popup_callback);
 | 
				
			||||||
 | 
					    popup_enable_timeout(popup);
 | 
				
			||||||
 | 
					    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const bool nfc_scene_restore_original_on_event(void* context, SceneManagerEvent event) {
 | 
				
			||||||
 | 
					    Nfc* nfc = (Nfc*)context;
 | 
				
			||||||
 | 
					    bool consumed = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if(event.type == SceneManagerEventTypeCustom) {
 | 
				
			||||||
 | 
					        if(event.event == SCENE_RESTORE_ORIGINAL_CUSTOM_EVENT) {
 | 
				
			||||||
 | 
					            consumed = scene_manager_previous_scene(nfc->scene_manager);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return consumed;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const void nfc_scene_restore_original_on_exit(void* context) {
 | 
				
			||||||
 | 
					    Nfc* nfc = (Nfc*)context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 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);
 | 
				
			||||||
 | 
					    popup_set_callback(popup, NULL);
 | 
				
			||||||
 | 
					    popup_set_context(popup, NULL);
 | 
				
			||||||
 | 
					    popup_set_timeout(popup, 0);
 | 
				
			||||||
 | 
					    popup_disable_timeout(popup);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -5,6 +5,7 @@ enum SubmenuIndex {
 | 
				
			|||||||
    SubmenuIndexEdit,
 | 
					    SubmenuIndexEdit,
 | 
				
			||||||
    SubmenuIndexDelete,
 | 
					    SubmenuIndexDelete,
 | 
				
			||||||
    SubmenuIndexInfo,
 | 
					    SubmenuIndexInfo,
 | 
				
			||||||
 | 
					    SubmenuIndexRestoreOriginal,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void nfc_scene_saved_menu_submenu_callback(void* context, uint32_t index) {
 | 
					void nfc_scene_saved_menu_submenu_callback(void* context, uint32_t index) {
 | 
				
			||||||
@ -29,36 +30,52 @@ const void nfc_scene_saved_menu_on_enter(void* context) {
 | 
				
			|||||||
        submenu, "Info", SubmenuIndexInfo, nfc_scene_saved_menu_submenu_callback, nfc);
 | 
					        submenu, "Info", SubmenuIndexInfo, nfc_scene_saved_menu_submenu_callback, nfc);
 | 
				
			||||||
    submenu_set_selected_item(
 | 
					    submenu_set_selected_item(
 | 
				
			||||||
        nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneSavedMenu));
 | 
					        nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneSavedMenu));
 | 
				
			||||||
 | 
					    if(nfc->dev.shadow_file_exist) {
 | 
				
			||||||
 | 
					        submenu_add_item(
 | 
				
			||||||
 | 
					            submenu,
 | 
				
			||||||
 | 
					            "Restore original",
 | 
				
			||||||
 | 
					            SubmenuIndexRestoreOriginal,
 | 
				
			||||||
 | 
					            nfc_scene_saved_menu_submenu_callback,
 | 
				
			||||||
 | 
					            nfc);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
 | 
					    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
 | 
					const bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
 | 
				
			||||||
    Nfc* nfc = (Nfc*)context;
 | 
					    Nfc* nfc = (Nfc*)context;
 | 
				
			||||||
 | 
					    bool consumed = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if(event.type == SceneManagerEventTypeCustom) {
 | 
					    if(event.type == SceneManagerEventTypeCustom) {
 | 
				
			||||||
 | 
					        scene_manager_set_scene_state(nfc->scene_manager, NfcSceneSavedMenu, event.event);
 | 
				
			||||||
        if(event.event == SubmenuIndexEmulate) {
 | 
					        if(event.event == SubmenuIndexEmulate) {
 | 
				
			||||||
            scene_manager_set_scene_state(
 | 
					            if(nfc->dev.format == NfcDeviceSaveFormatMifareUl) {
 | 
				
			||||||
                nfc->scene_manager, NfcSceneSavedMenu, SubmenuIndexEmulate);
 | 
					                scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl);
 | 
				
			||||||
            scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
 | 
					            } else {
 | 
				
			||||||
            return true;
 | 
					                scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            consumed = true;
 | 
				
			||||||
        } else if(event.event == SubmenuIndexEdit) {
 | 
					        } else if(event.event == SubmenuIndexEdit) {
 | 
				
			||||||
            scene_manager_set_scene_state(nfc->scene_manager, NfcSceneSavedMenu, SubmenuIndexEdit);
 | 
					 | 
				
			||||||
            scene_manager_next_scene(nfc->scene_manager, NfcSceneSetUid);
 | 
					            scene_manager_next_scene(nfc->scene_manager, NfcSceneSetUid);
 | 
				
			||||||
            return true;
 | 
					            consumed = true;
 | 
				
			||||||
        } else if(event.event == SubmenuIndexDelete) {
 | 
					        } else if(event.event == SubmenuIndexDelete) {
 | 
				
			||||||
            scene_manager_set_scene_state(
 | 
					 | 
				
			||||||
                nfc->scene_manager, NfcSceneSavedMenu, SubmenuIndexDelete);
 | 
					 | 
				
			||||||
            scene_manager_next_scene(nfc->scene_manager, NfcSceneDelete);
 | 
					            scene_manager_next_scene(nfc->scene_manager, NfcSceneDelete);
 | 
				
			||||||
            return true;
 | 
					            consumed = true;
 | 
				
			||||||
        } else if(event.event == SubmenuIndexInfo) {
 | 
					        } else if(event.event == SubmenuIndexInfo) {
 | 
				
			||||||
            scene_manager_set_scene_state(nfc->scene_manager, NfcSceneSavedMenu, SubmenuIndexInfo);
 | 
					 | 
				
			||||||
            scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo);
 | 
					            scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo);
 | 
				
			||||||
            return true;
 | 
					            consumed = true;
 | 
				
			||||||
 | 
					        } else if(event.event == SubmenuIndexRestoreOriginal) {
 | 
				
			||||||
 | 
					            if(!nfc_device_restore(&nfc->dev)) {
 | 
				
			||||||
 | 
					                scene_manager_search_and_switch_to_previous_scene(
 | 
				
			||||||
 | 
					                    nfc->scene_manager, NfcSceneStart);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                scene_manager_next_scene(nfc->scene_manager, NfcSceneRestoreOriginal);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            consumed = true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return false;
 | 
					    return consumed;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const void nfc_scene_saved_menu_on_exit(void* context) {
 | 
					const void nfc_scene_saved_menu_on_exit(void* context) {
 | 
				
			||||||
 | 
				
			|||||||
@ -86,9 +86,8 @@ bool furi_hal_nfc_detect(rfalNfcDevice **dev_list, uint8_t* dev_cnt, uint32_t ti
 | 
				
			|||||||
    return true;
 | 
					    return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool furi_hal_nfc_listen(uint8_t* uid, uint8_t uid_len, uint8_t* atqa, uint8_t sak, uint32_t timeout) {
 | 
					bool furi_hal_nfc_listen(uint8_t* uid, uint8_t uid_len, uint8_t* atqa, uint8_t sak, bool activate_after_sak, uint32_t timeout) {
 | 
				
			||||||
    rfalNfcState state = rfalNfcGetState();
 | 
					    rfalNfcState state = rfalNfcGetState();
 | 
				
			||||||
 | 
					 | 
				
			||||||
    if(state == RFAL_NFC_STATE_NOTINIT) {
 | 
					    if(state == RFAL_NFC_STATE_NOTINIT) {
 | 
				
			||||||
        rfalNfcInitialize();
 | 
					        rfalNfcInitialize();
 | 
				
			||||||
    } else if(state >= RFAL_NFC_STATE_ACTIVATED) {
 | 
					    } else if(state >= RFAL_NFC_STATE_ACTIVATED) {
 | 
				
			||||||
@ -107,6 +106,7 @@ bool furi_hal_nfc_listen(uint8_t* uid, uint8_t uid_len, uint8_t* atqa, uint8_t s
 | 
				
			|||||||
        .maxBR = RFAL_BR_KEEP,
 | 
					        .maxBR = RFAL_BR_KEEP,
 | 
				
			||||||
        .GBLen = RFAL_NFCDEP_GB_MAX_LEN,
 | 
					        .GBLen = RFAL_NFCDEP_GB_MAX_LEN,
 | 
				
			||||||
        .notifyCb = NULL,
 | 
					        .notifyCb = NULL,
 | 
				
			||||||
 | 
					        .activate_after_sak = activate_after_sak,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    params.lmConfigPA.nfcidLen = uid_len;
 | 
					    params.lmConfigPA.nfcidLen = uid_len;
 | 
				
			||||||
    memcpy(params.lmConfigPA.nfcid, uid, uid_len);
 | 
					    memcpy(params.lmConfigPA.nfcid, uid, uid_len);
 | 
				
			||||||
@ -119,7 +119,6 @@ bool furi_hal_nfc_listen(uint8_t* uid, uint8_t uid_len, uint8_t* atqa, uint8_t s
 | 
				
			|||||||
    while(state != RFAL_NFC_STATE_ACTIVATED) {
 | 
					    while(state != RFAL_NFC_STATE_ACTIVATED) {
 | 
				
			||||||
        rfalNfcWorker();
 | 
					        rfalNfcWorker();
 | 
				
			||||||
        state = rfalNfcGetState();
 | 
					        state = rfalNfcGetState();
 | 
				
			||||||
        FURI_LOG_D("HAL NFC", "Current state %d", state);
 | 
					 | 
				
			||||||
        if(DWT->CYCCNT - start > timeout * clocks_in_ms) {
 | 
					        if(DWT->CYCCNT - start > timeout * clocks_in_ms) {
 | 
				
			||||||
            rfalNfcDeactivate(true);
 | 
					            rfalNfcDeactivate(true);
 | 
				
			||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
@ -129,6 +128,11 @@ bool furi_hal_nfc_listen(uint8_t* uid, uint8_t uid_len, uint8_t* atqa, uint8_t s
 | 
				
			|||||||
    return true;
 | 
					    return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool furi_hal_nfc_get_first_frame(uint8_t** rx_buff, uint16_t** rx_len) {
 | 
				
			||||||
 | 
					    ReturnCode ret = rfalNfcDataExchangeStart(NULL, 0, rx_buff, rx_len, 0);
 | 
				
			||||||
 | 
					    return ret == ERR_NONE;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ReturnCode furi_hal_nfc_data_exchange(uint8_t* tx_buff, uint16_t tx_len, uint8_t** rx_buff, uint16_t** rx_len, bool deactivate) {
 | 
					ReturnCode furi_hal_nfc_data_exchange(uint8_t* tx_buff, uint16_t tx_len, uint8_t** rx_buff, uint16_t** rx_len, bool deactivate) {
 | 
				
			||||||
    furi_assert(rx_buff);
 | 
					    furi_assert(rx_buff);
 | 
				
			||||||
    furi_assert(rx_len);
 | 
					    furi_assert(rx_len);
 | 
				
			||||||
@ -144,7 +148,6 @@ ReturnCode furi_hal_nfc_data_exchange(uint8_t* tx_buff, uint16_t tx_len, uint8_t
 | 
				
			|||||||
        rfalNfcWorker();
 | 
					        rfalNfcWorker();
 | 
				
			||||||
        state = rfalNfcGetState();
 | 
					        state = rfalNfcGetState();
 | 
				
			||||||
        ret = rfalNfcDataExchangeGetStatus();
 | 
					        ret = rfalNfcDataExchangeGetStatus();
 | 
				
			||||||
        FURI_LOG_D("HAL NFC", "Nfc st: %d Data st: %d", state, ret);
 | 
					 | 
				
			||||||
        if(ret > ERR_SLEEP_REQ) {
 | 
					        if(ret > ERR_SLEEP_REQ) {
 | 
				
			||||||
            return ret;
 | 
					            return ret;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -49,7 +49,12 @@ bool furi_hal_nfc_detect(rfalNfcDevice** dev_list, uint8_t* dev_cnt, uint32_t ti
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * NFC listen
 | 
					 * NFC listen
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
bool furi_hal_nfc_listen(uint8_t* uid, uint8_t uid_len, uint8_t* atqa, uint8_t sak, uint32_t timeout);
 | 
					bool furi_hal_nfc_listen(uint8_t* uid, uint8_t uid_len, uint8_t* atqa, uint8_t sak, bool activate_after_sak, uint32_t timeout);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Get first command from reader after activation in emulation mode
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					bool furi_hal_nfc_get_first_frame(uint8_t** rx_buff, uint16_t** rx_len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * NFC data exchange
 | 
					 * NFC data exchange
 | 
				
			||||||
 | 
				
			|||||||
@ -213,6 +213,8 @@ typedef struct{
 | 
				
			|||||||
    bool               wakeupEnabled;                   /*!< Enable Wake-Up mode before polling                    */
 | 
					    bool               wakeupEnabled;                   /*!< Enable Wake-Up mode before polling                    */
 | 
				
			||||||
    bool               wakeupConfigDefault;             /*!< Wake-Up mode default configuration                    */
 | 
					    bool               wakeupConfigDefault;             /*!< Wake-Up mode default configuration                    */
 | 
				
			||||||
    rfalWakeUpConfig   wakeupConfig;                    /*!< Wake-Up mode configuration                            */
 | 
					    rfalWakeUpConfig   wakeupConfig;                    /*!< Wake-Up mode configuration                            */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool               activate_after_sak; // Set device to Active mode after SAK responce
 | 
				
			||||||
}rfalNfcDiscoverParam;
 | 
					}rfalNfcDiscoverParam;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -575,6 +575,7 @@ ReturnCode rfalNfcDataExchangeStart( uint8_t *txData, uint16_t txDataLen, uint8_
 | 
				
			|||||||
            *rvdLen = (uint16_t*)&gNfcDev.rxLen;
 | 
					            *rvdLen = (uint16_t*)&gNfcDev.rxLen;
 | 
				
			||||||
            *rxData = (uint8_t*)(  (gNfcDev.activeDev->rfInterface == RFAL_NFC_INTERFACE_ISODEP) ? gNfcDev.rxBuf.isoDepBuf.apdu : 
 | 
					            *rxData = (uint8_t*)(  (gNfcDev.activeDev->rfInterface == RFAL_NFC_INTERFACE_ISODEP) ? gNfcDev.rxBuf.isoDepBuf.apdu : 
 | 
				
			||||||
                                  ((gNfcDev.activeDev->rfInterface == RFAL_NFC_INTERFACE_NFCDEP) ? gNfcDev.rxBuf.nfcDepBuf.pdu  : gNfcDev.rxBuf.rfBuf));
 | 
					                                  ((gNfcDev.activeDev->rfInterface == RFAL_NFC_INTERFACE_NFCDEP) ? gNfcDev.rxBuf.nfcDepBuf.pdu  : gNfcDev.rxBuf.rfBuf));
 | 
				
			||||||
 | 
					            gNfcDev.state = RFAL_NFC_STATE_DATAEXCHANGE_DONE;
 | 
				
			||||||
            return ERR_NONE;
 | 
					            return ERR_NONE;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
@ -1579,6 +1580,7 @@ static ReturnCode rfalNfcListenActivation( void )
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    lmSt = rfalListenGetState( &isDataRcvd, &bitRate );
 | 
					    lmSt = rfalListenGetState( &isDataRcvd, &bitRate );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    switch(lmSt)
 | 
					    switch(lmSt)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1595,6 +1597,12 @@ static ReturnCode rfalNfcListenActivation( void )
 | 
				
			|||||||
                    /* Set the Listen Mode in Sleep state */
 | 
					                    /* Set the Listen Mode in Sleep state */
 | 
				
			||||||
                    EXIT_ON_ERR( ret, rfalListenSleepStart( RFAL_LM_STATE_SLEEP_A, gNfcDev.rxBuf.rfBuf, sizeof(gNfcDev.rxBuf.rfBuf), &gNfcDev.rxLen ) );
 | 
					                    EXIT_ON_ERR( ret, rfalListenSleepStart( RFAL_LM_STATE_SLEEP_A, gNfcDev.rxBuf.rfBuf, sizeof(gNfcDev.rxBuf.rfBuf), &gNfcDev.rxLen ) );
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                else if(gNfcDev.disc.activate_after_sak) {
 | 
				
			||||||
 | 
					                    gNfcDev.devList->type = RFAL_NFC_POLL_TYPE_NFCA;
 | 
				
			||||||
 | 
					                    rfalListenSetState(RFAL_LM_STATE_ACTIVE_A);
 | 
				
			||||||
 | 
					                    return ERR_NONE;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
            #if RFAL_FEATURE_ISO_DEP && RFAL_FEATURE_ISO_DEP_LISTEN
 | 
					            #if RFAL_FEATURE_ISO_DEP && RFAL_FEATURE_ISO_DEP_LISTEN
 | 
				
			||||||
                /* Check if received data is a valid RATS */
 | 
					                /* Check if received data is a valid RATS */
 | 
				
			||||||
 | 
				
			|||||||
@ -137,3 +137,94 @@ uint16_t mf_ul_prepare_write(uint8_t* dest, uint16_t page_addr, uint32_t data) {
 | 
				
			|||||||
    dest[5] = (uint8_t) data;
 | 
					    dest[5] = (uint8_t) data;
 | 
				
			||||||
    return 6;
 | 
					    return 6;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void mf_ul_prepare_emulation(MifareUlDevice* mf_ul_emulate, MifareUlData* data) {
 | 
				
			||||||
 | 
					    mf_ul_emulate->data = *data;
 | 
				
			||||||
 | 
					    mf_ul_emulate->data_changed = false;
 | 
				
			||||||
 | 
					    if(data->version.storage_size == 0) {
 | 
				
			||||||
 | 
					        mf_ul_emulate->type = MfUltralightTypeUnknown;
 | 
				
			||||||
 | 
					        mf_ul_emulate->support_fast_read = false;
 | 
				
			||||||
 | 
					    } else if(data->version.storage_size == 0x0B) {
 | 
				
			||||||
 | 
					        mf_ul_emulate->type = MfUltralightTypeUL11;
 | 
				
			||||||
 | 
					        mf_ul_emulate->support_fast_read = true;
 | 
				
			||||||
 | 
					    } else if(data->version.storage_size == 0x0E) {
 | 
				
			||||||
 | 
					        mf_ul_emulate->type = MfUltralightTypeUL21;
 | 
				
			||||||
 | 
					        mf_ul_emulate->support_fast_read = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uint16_t mf_ul_prepare_emulation_response(uint8_t* buff_rx, uint16_t len_rx, uint8_t* buff_tx, MifareUlDevice* mf_ul_emulate) {
 | 
				
			||||||
 | 
					    uint8_t cmd = buff_rx[0];
 | 
				
			||||||
 | 
					    uint16_t page_num = mf_ul_emulate->data.data_size / 4;
 | 
				
			||||||
 | 
					    uint16_t tx_len = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if(cmd == MF_UL_GET_VERSION_CMD) {
 | 
				
			||||||
 | 
					        if(mf_ul_emulate->type != MfUltralightTypeUnknown) {
 | 
				
			||||||
 | 
					            tx_len = sizeof(mf_ul_emulate->data.version);
 | 
				
			||||||
 | 
					            memcpy(buff_tx, &mf_ul_emulate->data.version, tx_len);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else if(cmd == MF_UL_READ_CMD) {
 | 
				
			||||||
 | 
					        uint8_t start_page = buff_rx[1];
 | 
				
			||||||
 | 
					        if(start_page < page_num) {
 | 
				
			||||||
 | 
					            tx_len = 16;
 | 
				
			||||||
 | 
					            if(start_page + 4 > page_num) {
 | 
				
			||||||
 | 
					                // Handle roll-over mechanism
 | 
				
			||||||
 | 
					                uint8_t end_pages_num = page_num - start_page;
 | 
				
			||||||
 | 
					                memcpy(buff_tx, &mf_ul_emulate->data.data[start_page * 4], end_pages_num * 4);
 | 
				
			||||||
 | 
					                memcpy(&buff_tx[end_pages_num * 4], mf_ul_emulate->data.data, (4 - end_pages_num) * 4);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                memcpy(buff_tx, &mf_ul_emulate->data.data[start_page * 4], tx_len);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else if(cmd == MF_UL_FAST_READ_CMD) {
 | 
				
			||||||
 | 
					        if(mf_ul_emulate->support_fast_read) {
 | 
				
			||||||
 | 
					            uint8_t start_page = buff_rx[1];
 | 
				
			||||||
 | 
					            uint8_t end_page = buff_rx[2];
 | 
				
			||||||
 | 
					            if((start_page < page_num) &&
 | 
				
			||||||
 | 
					               (end_page < page_num) && (start_page < end_page)) {
 | 
				
			||||||
 | 
					                tx_len = (end_page - start_page) * 4;
 | 
				
			||||||
 | 
					                memcpy(buff_tx, &mf_ul_emulate->data.data[start_page * 4], tx_len);
 | 
				
			||||||
 | 
					               }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else if(cmd == MF_UL_WRITE) {
 | 
				
			||||||
 | 
					        uint8_t write_page = buff_rx[1];
 | 
				
			||||||
 | 
					        if((write_page > 1) && (write_page < page_num - 2)) {
 | 
				
			||||||
 | 
					            memcpy(&mf_ul_emulate->data.data[write_page * 4], &buff_rx[2], 4);
 | 
				
			||||||
 | 
					            mf_ul_emulate->data_changed = true;
 | 
				
			||||||
 | 
					            // TODO make 4-bit ACK
 | 
				
			||||||
 | 
					            buff_tx[0] = 0x0A;
 | 
				
			||||||
 | 
					            tx_len = 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else if(cmd == MF_UL_READ_CNT) {
 | 
				
			||||||
 | 
					        uint8_t cnt_num = buff_rx[1];
 | 
				
			||||||
 | 
					        if(cnt_num < 3) {
 | 
				
			||||||
 | 
					            buff_tx[0] = mf_ul_emulate->data.counter[cnt_num] >> 16;
 | 
				
			||||||
 | 
					            buff_tx[1] = mf_ul_emulate->data.counter[cnt_num] >> 8;
 | 
				
			||||||
 | 
					            buff_tx[2] = mf_ul_emulate->data.counter[cnt_num];
 | 
				
			||||||
 | 
					            tx_len = 3;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else if(cmd == MF_UL_INC_CNT) {
 | 
				
			||||||
 | 
					        uint8_t cnt_num = buff_rx[1];
 | 
				
			||||||
 | 
					        uint32_t inc = (buff_rx[2] | (buff_rx[3] << 8) | (buff_rx[4] << 16));
 | 
				
			||||||
 | 
					        if((cnt_num < 3) && (mf_ul_emulate->data.counter[cnt_num] + inc < 0x00FFFFFF)) {
 | 
				
			||||||
 | 
					            mf_ul_emulate->data.counter[cnt_num] += inc;
 | 
				
			||||||
 | 
					            mf_ul_emulate->data_changed = true;
 | 
				
			||||||
 | 
					            // TODO make 4-bit ACK
 | 
				
			||||||
 | 
					            buff_tx[0] = 0x0A;
 | 
				
			||||||
 | 
					            tx_len = 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else if(cmd == MF_UL_READ_SIG) {
 | 
				
			||||||
 | 
					        // Check 2nd byte = 0x00 - RFU
 | 
				
			||||||
 | 
					        if(buff_rx[1] == 0x00) {
 | 
				
			||||||
 | 
					            tx_len = sizeof(mf_ul_emulate->data.signature);
 | 
				
			||||||
 | 
					            memcpy(buff_tx, mf_ul_emulate->data.signature, tx_len);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else if(cmd == MF_UL_CHECK_TEARING) {
 | 
				
			||||||
 | 
					        uint8_t cnt_num = buff_rx[1];
 | 
				
			||||||
 | 
					        if(cnt_num < 3) {
 | 
				
			||||||
 | 
					            buff_tx[0] = mf_ul_emulate->data.tearing[cnt_num];
 | 
				
			||||||
 | 
					            tx_len = 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return tx_len;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -65,6 +65,7 @@ typedef struct {
 | 
				
			|||||||
    uint8_t pages_to_read;
 | 
					    uint8_t pages_to_read;
 | 
				
			||||||
    uint8_t pages_readed;
 | 
					    uint8_t pages_readed;
 | 
				
			||||||
    bool support_fast_read;
 | 
					    bool support_fast_read;
 | 
				
			||||||
 | 
					    bool data_changed;
 | 
				
			||||||
    MifareUlData data;
 | 
					    MifareUlData data;
 | 
				
			||||||
} MifareUlDevice;
 | 
					} MifareUlDevice;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -93,3 +94,5 @@ void mf_ul_parse_fast_read_response(uint8_t* buff, uint8_t start_page, uint8_t e
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
uint16_t mf_ul_prepare_write(uint8_t* dest, uint16_t page_addr, uint32_t data);
 | 
					uint16_t mf_ul_prepare_write(uint8_t* dest, uint16_t page_addr, uint32_t data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void mf_ul_prepare_emulation(MifareUlDevice* mf_ul_emulate, MifareUlData* data);
 | 
				
			||||||
 | 
					uint16_t mf_ul_prepare_emulation_response(uint8_t* buff_rx, uint16_t len_rx, uint8_t* buff_tx, MifareUlDevice* mf_ul_emulate);
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user