 c00776ca22
			
		
	
	
		c00776ca22
		
			
		
	
	
	
	
		
			
			* drivers: expose st25r3916 driver API * nfc poller: add start with custom callback * mf classic: rework sync API with poller custom start * mf ultralight: rework sync API with poller custom start * iso14443_3a poller: remove unused col res state * nfc: rework nfc poller custom start * mf ultralight: rename sync API * mf classic: rename sync API * iso14443-3a: rename sync API * nfc: remove async prefix in internal functions * nfc: expose internal API * nfc: fix sync api include and docs * targets: fix f18 build * nfc: rework NfcGenericEventEx type * nfc poller: add documentation * iso14443-3a poller: add documentation * felica poller: add documentation * iso14443_3b poller: add documentation * so14443_4a poller: add documentation * iso14443_4b poller: add documentation * iso15693 poller: add documentation * slix poller: add documentation * mf desfire poller: add documentation * mf ultralight poller: fix API and add documentation * mf classic poller: add documentation Co-authored-by: あく <alleteam@gmail.com>
		
			
				
	
	
		
			477 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			477 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include "mf_desfire_poller_i.h"
 | |
| 
 | |
| #include <furi.h>
 | |
| 
 | |
| #include "mf_desfire_i.h"
 | |
| 
 | |
| #define TAG "MfDesfirePoller"
 | |
| 
 | |
| MfDesfireError mf_desfire_process_error(Iso14443_4aError error) {
 | |
|     switch(error) {
 | |
|     case Iso14443_4aErrorNone:
 | |
|         return MfDesfireErrorNone;
 | |
|     case Iso14443_4aErrorNotPresent:
 | |
|         return MfDesfireErrorNotPresent;
 | |
|     case Iso14443_4aErrorTimeout:
 | |
|         return MfDesfireErrorTimeout;
 | |
|     default:
 | |
|         return MfDesfireErrorProtocol;
 | |
|     }
 | |
| }
 | |
| 
 | |
| MfDesfireError mf_desfire_send_chunks(
 | |
|     MfDesfirePoller* instance,
 | |
|     const BitBuffer* tx_buffer,
 | |
|     BitBuffer* rx_buffer) {
 | |
|     furi_assert(instance);
 | |
|     furi_assert(instance->iso14443_4a_poller);
 | |
|     furi_assert(instance->tx_buffer);
 | |
|     furi_assert(instance->rx_buffer);
 | |
|     furi_assert(tx_buffer);
 | |
|     furi_assert(rx_buffer);
 | |
| 
 | |
|     MfDesfireError error = MfDesfireErrorNone;
 | |
| 
 | |
|     do {
 | |
|         Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block(
 | |
|             instance->iso14443_4a_poller, tx_buffer, instance->rx_buffer);
 | |
| 
 | |
|         if(iso14443_4a_error != Iso14443_4aErrorNone) {
 | |
|             error = mf_desfire_process_error(iso14443_4a_error);
 | |
|             break;
 | |
|         }
 | |
| 
 | |
|         bit_buffer_reset(instance->tx_buffer);
 | |
|         bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_FLAG_HAS_NEXT);
 | |
| 
 | |
|         if(bit_buffer_get_size_bytes(instance->rx_buffer) > sizeof(uint8_t)) {
 | |
|             bit_buffer_copy_right(rx_buffer, instance->rx_buffer, sizeof(uint8_t));
 | |
|         } else {
 | |
|             bit_buffer_reset(rx_buffer);
 | |
|         }
 | |
| 
 | |
|         while(bit_buffer_starts_with_byte(instance->rx_buffer, MF_DESFIRE_FLAG_HAS_NEXT)) {
 | |
|             Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block(
 | |
|                 instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer);
 | |
| 
 | |
|             if(iso14443_4a_error != Iso14443_4aErrorNone) {
 | |
|                 error = mf_desfire_process_error(iso14443_4a_error);
 | |
|                 break;
 | |
|             }
 | |
| 
 | |
|             const size_t rx_size = bit_buffer_get_size_bytes(instance->rx_buffer);
 | |
|             const size_t rx_capacity_remaining =
 | |
|                 bit_buffer_get_capacity_bytes(rx_buffer) - bit_buffer_get_size_bytes(rx_buffer);
 | |
| 
 | |
|             if(rx_size <= rx_capacity_remaining) {
 | |
|                 bit_buffer_append_right(rx_buffer, instance->rx_buffer, sizeof(uint8_t));
 | |
|             } else {
 | |
|                 FURI_LOG_W(TAG, "RX buffer overflow: ignoring %zu bytes", rx_size);
 | |
|             }
 | |
|         }
 | |
|     } while(false);
 | |
| 
 | |
|     return error;
 | |
| }
 | |
| 
 | |
| MfDesfireError mf_desfire_poller_read_version(MfDesfirePoller* instance, MfDesfireVersion* data) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     bit_buffer_reset(instance->input_buffer);
 | |
|     bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_VERSION);
 | |
| 
 | |
|     MfDesfireError error;
 | |
| 
 | |
|     do {
 | |
|         error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
 | |
| 
 | |
|         if(error != MfDesfireErrorNone) break;
 | |
| 
 | |
|         if(!mf_desfire_version_parse(data, instance->result_buffer)) {
 | |
|             error = MfDesfireErrorProtocol;
 | |
|         }
 | |
|     } while(false);
 | |
| 
 | |
|     return error;
 | |
| }
 | |
| 
 | |
| MfDesfireError
 | |
|     mf_desfire_poller_read_free_memory(MfDesfirePoller* instance, MfDesfireFreeMemory* data) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     bit_buffer_reset(instance->input_buffer);
 | |
|     bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_FREE_MEMORY);
 | |
| 
 | |
|     MfDesfireError error;
 | |
| 
 | |
|     do {
 | |
|         error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
 | |
| 
 | |
|         if(error != MfDesfireErrorNone) break;
 | |
| 
 | |
|         if(!mf_desfire_free_memory_parse(data, instance->result_buffer)) {
 | |
|             error = MfDesfireErrorProtocol;
 | |
|         }
 | |
|     } while(false);
 | |
| 
 | |
|     return error;
 | |
| }
 | |
| 
 | |
| MfDesfireError
 | |
|     mf_desfire_poller_read_key_settings(MfDesfirePoller* instance, MfDesfireKeySettings* data) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     bit_buffer_reset(instance->input_buffer);
 | |
|     bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_KEY_SETTINGS);
 | |
| 
 | |
|     MfDesfireError error;
 | |
| 
 | |
|     do {
 | |
|         error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
 | |
| 
 | |
|         if(error != MfDesfireErrorNone) break;
 | |
| 
 | |
|         if(!mf_desfire_key_settings_parse(data, instance->result_buffer)) {
 | |
|             error = MfDesfireErrorProtocol;
 | |
|         }
 | |
|     } while(false);
 | |
| 
 | |
|     return error;
 | |
| }
 | |
| 
 | |
| MfDesfireError mf_desfire_poller_read_key_versions(
 | |
|     MfDesfirePoller* instance,
 | |
|     SimpleArray* data,
 | |
|     uint32_t count) {
 | |
|     furi_assert(instance);
 | |
|     furi_assert(count > 0);
 | |
| 
 | |
|     simple_array_init(data, count);
 | |
| 
 | |
|     bit_buffer_set_size_bytes(instance->input_buffer, sizeof(uint8_t) * 2);
 | |
|     bit_buffer_set_byte(instance->input_buffer, 0, MF_DESFIRE_CMD_GET_KEY_VERSION);
 | |
| 
 | |
|     MfDesfireError error = MfDesfireErrorNone;
 | |
| 
 | |
|     for(uint32_t i = 0; i < count; ++i) {
 | |
|         bit_buffer_set_byte(instance->input_buffer, 1, i);
 | |
| 
 | |
|         error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
 | |
| 
 | |
|         if(error != MfDesfireErrorNone) break;
 | |
| 
 | |
|         if(!mf_desfire_key_version_parse(simple_array_get(data, i), instance->result_buffer)) {
 | |
|             error = MfDesfireErrorProtocol;
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return error;
 | |
| }
 | |
| 
 | |
| MfDesfireError
 | |
|     mf_desfire_poller_read_application_ids(MfDesfirePoller* instance, SimpleArray* data) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     bit_buffer_reset(instance->input_buffer);
 | |
|     bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_APPLICATION_IDS);
 | |
| 
 | |
|     MfDesfireError error;
 | |
| 
 | |
|     do {
 | |
|         error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
 | |
| 
 | |
|         if(error != MfDesfireErrorNone) break;
 | |
| 
 | |
|         const uint32_t app_id_count =
 | |
|             bit_buffer_get_size_bytes(instance->result_buffer) / sizeof(MfDesfireApplicationId);
 | |
|         if(app_id_count == 0) break;
 | |
| 
 | |
|         simple_array_init(data, app_id_count);
 | |
| 
 | |
|         for(uint32_t i = 0; i < app_id_count; ++i) {
 | |
|             if(!mf_desfire_application_id_parse(
 | |
|                    simple_array_get(data, i), i, instance->result_buffer)) {
 | |
|                 error = MfDesfireErrorProtocol;
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|     } while(false);
 | |
| 
 | |
|     return error;
 | |
| }
 | |
| 
 | |
| MfDesfireError mf_desfire_poller_select_application(
 | |
|     MfDesfirePoller* instance,
 | |
|     const MfDesfireApplicationId* id) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     bit_buffer_reset(instance->input_buffer);
 | |
|     bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_SELECT_APPLICATION);
 | |
|     bit_buffer_append_bytes(
 | |
|         instance->input_buffer, (const uint8_t*)id, sizeof(MfDesfireApplicationId));
 | |
| 
 | |
|     MfDesfireError error =
 | |
|         mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
 | |
| 
 | |
|     return error;
 | |
| }
 | |
| 
 | |
| MfDesfireError mf_desfire_poller_read_file_ids(MfDesfirePoller* instance, SimpleArray* data) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     bit_buffer_reset(instance->input_buffer);
 | |
|     bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_FILE_IDS);
 | |
| 
 | |
|     MfDesfireError error;
 | |
| 
 | |
|     do {
 | |
|         error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
 | |
| 
 | |
|         if(error != MfDesfireErrorNone) break;
 | |
| 
 | |
|         const uint32_t id_count =
 | |
|             bit_buffer_get_size_bytes(instance->result_buffer) / sizeof(MfDesfireFileId);
 | |
| 
 | |
|         if(id_count == 0) break;
 | |
|         simple_array_init(data, id_count);
 | |
| 
 | |
|         for(uint32_t i = 0; i < id_count; ++i) {
 | |
|             if(!mf_desfire_file_id_parse(simple_array_get(data, i), i, instance->result_buffer)) {
 | |
|                 error = MfDesfireErrorProtocol;
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|     } while(false);
 | |
| 
 | |
|     return error;
 | |
| }
 | |
| 
 | |
| MfDesfireError mf_desfire_poller_read_file_settings(
 | |
|     MfDesfirePoller* instance,
 | |
|     MfDesfireFileId id,
 | |
|     MfDesfireFileSettings* data) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     bit_buffer_reset(instance->input_buffer);
 | |
|     bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_FILE_SETTINGS);
 | |
|     bit_buffer_append_byte(instance->input_buffer, id);
 | |
| 
 | |
|     MfDesfireError error;
 | |
| 
 | |
|     do {
 | |
|         error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
 | |
| 
 | |
|         if(error != MfDesfireErrorNone) break;
 | |
| 
 | |
|         if(!mf_desfire_file_settings_parse(data, instance->result_buffer)) {
 | |
|             error = MfDesfireErrorProtocol;
 | |
|         }
 | |
|     } while(false);
 | |
| 
 | |
|     return error;
 | |
| }
 | |
| 
 | |
| MfDesfireError mf_desfire_poller_read_file_settings_multi(
 | |
|     MfDesfirePoller* instance,
 | |
|     const SimpleArray* file_ids,
 | |
|     SimpleArray* data) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     MfDesfireError error = MfDesfireErrorNone;
 | |
| 
 | |
|     const uint32_t file_id_count = simple_array_get_count(file_ids);
 | |
|     if(file_id_count > 0) {
 | |
|         simple_array_init(data, file_id_count);
 | |
|     }
 | |
| 
 | |
|     for(uint32_t i = 0; i < file_id_count; ++i) {
 | |
|         const MfDesfireFileId file_id = *(const MfDesfireFileId*)simple_array_cget(file_ids, i);
 | |
|         error = mf_desfire_poller_read_file_settings(instance, file_id, simple_array_get(data, i));
 | |
|         if(error != MfDesfireErrorNone) break;
 | |
|     }
 | |
| 
 | |
|     return error;
 | |
| }
 | |
| 
 | |
| MfDesfireError mf_desfire_poller_read_file_data(
 | |
|     MfDesfirePoller* instance,
 | |
|     MfDesfireFileId id,
 | |
|     uint32_t offset,
 | |
|     size_t size,
 | |
|     MfDesfireFileData* data) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     bit_buffer_reset(instance->input_buffer);
 | |
|     bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_READ_DATA);
 | |
|     bit_buffer_append_byte(instance->input_buffer, id);
 | |
|     bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&offset, 3);
 | |
|     bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&size, 3);
 | |
| 
 | |
|     MfDesfireError error;
 | |
| 
 | |
|     do {
 | |
|         error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
 | |
| 
 | |
|         if(error != MfDesfireErrorNone) break;
 | |
| 
 | |
|         if(!mf_desfire_file_data_parse(data, instance->result_buffer)) {
 | |
|             error = MfDesfireErrorProtocol;
 | |
|         }
 | |
|     } while(false);
 | |
| 
 | |
|     return error;
 | |
| }
 | |
| 
 | |
| MfDesfireError mf_desfire_poller_read_file_value(
 | |
|     MfDesfirePoller* instance,
 | |
|     MfDesfireFileId id,
 | |
|     MfDesfireFileData* data) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     bit_buffer_reset(instance->input_buffer);
 | |
|     bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_VALUE);
 | |
|     bit_buffer_append_byte(instance->input_buffer, id);
 | |
| 
 | |
|     MfDesfireError error;
 | |
| 
 | |
|     do {
 | |
|         error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
 | |
| 
 | |
|         if(error != MfDesfireErrorNone) break;
 | |
| 
 | |
|         if(!mf_desfire_file_data_parse(data, instance->result_buffer)) {
 | |
|             error = MfDesfireErrorProtocol;
 | |
|         }
 | |
|     } while(false);
 | |
| 
 | |
|     return error;
 | |
| }
 | |
| 
 | |
| MfDesfireError mf_desfire_poller_read_file_records(
 | |
|     MfDesfirePoller* instance,
 | |
|     MfDesfireFileId id,
 | |
|     uint32_t offset,
 | |
|     size_t size,
 | |
|     MfDesfireFileData* data) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     bit_buffer_reset(instance->input_buffer);
 | |
|     bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_READ_RECORDS);
 | |
|     bit_buffer_append_byte(instance->input_buffer, id);
 | |
|     bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&offset, 3);
 | |
|     bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&size, 3);
 | |
| 
 | |
|     MfDesfireError error;
 | |
| 
 | |
|     do {
 | |
|         error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
 | |
| 
 | |
|         if(error != MfDesfireErrorNone) break;
 | |
| 
 | |
|         if(!mf_desfire_file_data_parse(data, instance->result_buffer)) {
 | |
|             error = MfDesfireErrorProtocol;
 | |
|         }
 | |
|     } while(false);
 | |
| 
 | |
|     return error;
 | |
| }
 | |
| 
 | |
| MfDesfireError mf_desfire_poller_read_file_data_multi(
 | |
|     MfDesfirePoller* instance,
 | |
|     const SimpleArray* file_ids,
 | |
|     const SimpleArray* file_settings,
 | |
|     SimpleArray* data) {
 | |
|     furi_assert(instance);
 | |
|     furi_assert(simple_array_get_count(file_ids) == simple_array_get_count(file_settings));
 | |
| 
 | |
|     MfDesfireError error = MfDesfireErrorNone;
 | |
| 
 | |
|     const uint32_t file_id_count = simple_array_get_count(file_ids);
 | |
|     if(file_id_count > 0) {
 | |
|         simple_array_init(data, file_id_count);
 | |
|     }
 | |
| 
 | |
|     for(uint32_t i = 0; i < file_id_count; ++i) {
 | |
|         const MfDesfireFileId file_id = *(const MfDesfireFileId*)simple_array_cget(file_ids, i);
 | |
|         const MfDesfireFileSettings* file_settings_cur = simple_array_cget(file_settings, i);
 | |
|         const MfDesfireFileType file_type = file_settings_cur->type;
 | |
| 
 | |
|         MfDesfireFileData* file_data = simple_array_get(data, i);
 | |
| 
 | |
|         if(file_type == MfDesfireFileTypeStandard || file_type == MfDesfireFileTypeBackup) {
 | |
|             error = mf_desfire_poller_read_file_data(
 | |
|                 instance, file_id, 0, file_settings_cur->data.size, file_data);
 | |
|         } else if(file_type == MfDesfireFileTypeValue) {
 | |
|             error = mf_desfire_poller_read_file_value(instance, file_id, file_data);
 | |
|         } else if(
 | |
|             file_type == MfDesfireFileTypeLinearRecord ||
 | |
|             file_type == MfDesfireFileTypeCyclicRecord) {
 | |
|             error = mf_desfire_poller_read_file_records(
 | |
|                 instance, file_id, 0, file_settings_cur->data.size, file_data);
 | |
|         }
 | |
| 
 | |
|         if(error != MfDesfireErrorNone) break;
 | |
|     }
 | |
| 
 | |
|     return error;
 | |
| }
 | |
| 
 | |
| MfDesfireError
 | |
|     mf_desfire_poller_read_application(MfDesfirePoller* instance, MfDesfireApplication* data) {
 | |
|     furi_assert(instance);
 | |
|     furi_assert(data);
 | |
| 
 | |
|     MfDesfireError error;
 | |
| 
 | |
|     do {
 | |
|         error = mf_desfire_poller_read_key_settings(instance, &data->key_settings);
 | |
|         if(error != MfDesfireErrorNone) break;
 | |
| 
 | |
|         error = mf_desfire_poller_read_key_versions(
 | |
|             instance, data->key_versions, data->key_settings.max_keys);
 | |
|         if(error != MfDesfireErrorNone) break;
 | |
| 
 | |
|         error = mf_desfire_poller_read_file_ids(instance, data->file_ids);
 | |
|         if(error != MfDesfireErrorNone) break;
 | |
| 
 | |
|         error = mf_desfire_poller_read_file_settings_multi(
 | |
|             instance, data->file_ids, data->file_settings);
 | |
|         if(error != MfDesfireErrorNone) break;
 | |
| 
 | |
|         error = mf_desfire_poller_read_file_data_multi(
 | |
|             instance, data->file_ids, data->file_settings, data->file_data);
 | |
|         if(error != MfDesfireErrorNone) break;
 | |
| 
 | |
|     } while(false);
 | |
| 
 | |
|     return error;
 | |
| }
 | |
| 
 | |
| MfDesfireError mf_desfire_poller_read_applications(
 | |
|     MfDesfirePoller* instance,
 | |
|     const SimpleArray* app_ids,
 | |
|     SimpleArray* data) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     MfDesfireError error = MfDesfireErrorNone;
 | |
| 
 | |
|     const uint32_t app_id_count = simple_array_get_count(app_ids);
 | |
|     if(app_id_count > 0) {
 | |
|         simple_array_init(data, app_id_count);
 | |
|     }
 | |
| 
 | |
|     for(uint32_t i = 0; i < app_id_count; ++i) {
 | |
|         do {
 | |
|             error = mf_desfire_poller_select_application(instance, simple_array_cget(app_ids, i));
 | |
|             if(error != MfDesfireErrorNone) break;
 | |
| 
 | |
|             MfDesfireApplication* current_app = simple_array_get(data, i);
 | |
|             error = mf_desfire_poller_read_application(instance, current_app);
 | |
| 
 | |
|         } while(false);
 | |
|     }
 | |
| 
 | |
|     return error;
 | |
| }
 |