#include "felica_poller_i.h" #include #include #include #include #include #define TAG "FelicaPoller" ARRAY_DEF(felica_service_array, FelicaService, M_POD_OPLIST); // -V658 ARRAY_DEF(felica_area_array, FelicaArea, M_POD_OPLIST); // -V658 ARRAY_DEF(felica_public_block_array, FelicaPublicBlock, M_POD_OPLIST); // -V658 ARRAY_DEF(felica_system_array, FelicaSystem, M_POD_OPLIST); // -V658 typedef NfcCommand (*FelicaPollerReadHandler)(FelicaPoller* instance); const FelicaData* felica_poller_get_data(FelicaPoller* instance) { furi_assert(instance); furi_assert(instance->data); return instance->data; } static FelicaPoller* felica_poller_alloc(Nfc* nfc) { furi_assert(nfc); FelicaPoller* instance = malloc(sizeof(FelicaPoller)); instance->nfc = nfc; instance->tx_buffer = bit_buffer_alloc(FELICA_POLLER_MAX_BUFFER_SIZE); instance->rx_buffer = bit_buffer_alloc(FELICA_POLLER_MAX_BUFFER_SIZE); nfc_config(instance->nfc, NfcModePoller, NfcTechFelica); nfc_set_guard_time_us(instance->nfc, FELICA_GUARD_TIME_US); nfc_set_fdt_poll_fc(instance->nfc, FELICA_FDT_POLL_FC); nfc_set_fdt_poll_poll_us(instance->nfc, FELICA_POLL_POLL_MIN_US); mbedtls_des3_init(&instance->auth.des_context); instance->data = felica_alloc(); instance->felica_event.data = &instance->felica_event_data; instance->general_event.protocol = NfcProtocolFelica; instance->general_event.event_data = &instance->felica_event; instance->general_event.instance = instance; instance->systems_read = 0; instance->systems_total = 0; return instance; } static void felica_poller_free(FelicaPoller* instance) { furi_assert(instance); furi_assert(instance->tx_buffer); furi_assert(instance->rx_buffer); furi_assert(instance->data); mbedtls_des3_free(&instance->auth.des_context); bit_buffer_free(instance->tx_buffer); bit_buffer_free(instance->rx_buffer); felica_free(instance->data); free(instance); } static void felica_poller_set_callback(FelicaPoller* instance, NfcGenericCallback callback, void* context) { furi_assert(instance); furi_assert(callback); instance->callback = callback; instance->context = context; } NfcCommand felica_poller_state_handler_idle(FelicaPoller* instance) { FURI_LOG_D(TAG, "Idle"); felica_reset(instance->data); instance->state = FelicaPollerStateActivated; return NfcCommandContinue; } NfcCommand felica_poller_state_handler_activate(FelicaPoller* instance) { FURI_LOG_D(TAG, "Activate"); NfcCommand command = NfcCommandContinue; FelicaError error = felica_poller_activate(instance, instance->data); if(error == FelicaErrorNone) { furi_hal_random_fill_buf(instance->data->data.fs.rc.data, FELICA_DATA_BLOCK_SIZE); felica_get_workflow_type(instance->data); instance->felica_event.type = FelicaPollerEventTypeRequestAuthContext; instance->felica_event_data.auth_context = &instance->auth.context; instance->callback(instance->general_event, instance->context); switch(instance->data->workflow_type) { case FelicaStandard: instance->state = FelicaPollerStateListSystem; break; case FelicaLite: instance->state = FelicaPollerStateReadLiteBlocks; break; default: // Unimplemented instance->state = FelicaPollerStateReadSuccess; break; } bool skip_auth = instance->auth.context.skip_auth; if(!skip_auth) { instance->state = FelicaPollerStateAuthenticateInternal; } } else if(error != FelicaErrorTimeout) { instance->felica_event.type = FelicaPollerEventTypeError; instance->felica_event_data.error = error; instance->state = FelicaPollerStateReadFailed; } return command; } NfcCommand felica_poller_state_handler_list_system(FelicaPoller* instance) { FURI_LOG_D(TAG, "List System"); NfcCommand command = NfcCommandContinue; FelicaListSystemCodeCommandResponse* response_system_code; FelicaError error = felica_poller_list_system_code(instance, &response_system_code); instance->systems_total = response_system_code->system_count; simple_array_init(instance->data->systems, instance->systems_total); uint8_t* system_codes = response_system_code->system_code; for(uint8_t i = 0; i < instance->systems_total; i++) { FelicaSystem* system = simple_array_get(instance->data->systems, i); system->system_code = system_codes[i * 2] << 8 | system_codes[i * 2 + 1]; system->system_code_idx = i; } if(error == FelicaErrorNone) { instance->state = FelicaPollerStateSelectSystemIndex; } else if(error != FelicaErrorTimeout) { instance->felica_event.type = FelicaPollerEventTypeError; instance->felica_event_data.error = error; instance->state = FelicaPollerStateReadFailed; } return command; } NfcCommand felica_poller_state_handler_select_system_idx(FelicaPoller* instance) { FURI_LOG_D(TAG, "Select System Index %d", instance->systems_read); uint8_t system_index_mask = instance->systems_read << 4; instance->data->idm.data[0] &= 0x0F; instance->data->idm.data[0] |= system_index_mask; instance->state = FelicaPollerStateTraverseStandardSystem; return NfcCommandContinue; } NfcCommand felica_poller_state_handler_auth_internal(FelicaPoller* instance) { FURI_LOG_D(TAG, "Auth Internal"); felica_calculate_session_key( &instance->auth.des_context, instance->auth.context.card_key.data, instance->data->data.fs.rc.data, instance->auth.session_key.data); switch(instance->data->workflow_type) { case FelicaStandard: instance->state = FelicaPollerStateTraverseStandardSystem; break; case FelicaLite: instance->state = FelicaPollerStateReadLiteBlocks; break; default: // Unimplemented instance->state = FelicaPollerStateReadSuccess; break; } uint8_t blocks[3] = {FELICA_BLOCK_INDEX_RC, 0, 0}; FelicaPollerWriteCommandResponse* tx_resp; do { FelicaError error = felica_poller_write_blocks( instance, 1, blocks, instance->data->data.fs.rc.data, &tx_resp); if((error != FelicaErrorNone) || (tx_resp->SF1 != 0) || (tx_resp->SF2 != 0)) break; blocks[0] = FELICA_BLOCK_INDEX_ID; blocks[1] = FELICA_BLOCK_INDEX_WCNT; blocks[2] = FELICA_BLOCK_INDEX_MAC_A; FelicaPollerReadCommandResponse* rx_resp; error = felica_poller_read_blocks( instance, sizeof(blocks), blocks, FELICA_SERVICE_RO_ACCESS, &rx_resp); if(error != FelicaErrorNone || rx_resp->SF1 != 0 || rx_resp->SF2 != 0) break; if(felica_check_mac( &instance->auth.des_context, instance->auth.session_key.data, instance->data->data.fs.rc.data, blocks, rx_resp->block_count, rx_resp->data)) { instance->auth.context.auth_status.internal = true; instance->data->data.fs.wcnt.SF1 = 0; instance->data->data.fs.wcnt.SF2 = 0; memcpy( instance->data->data.fs.wcnt.data, rx_resp->data + FELICA_DATA_BLOCK_SIZE, FELICA_DATA_BLOCK_SIZE); instance->state = FelicaPollerStateAuthenticateExternal; } } while(false); return NfcCommandContinue; } NfcCommand felica_poller_state_handler_auth_external(FelicaPoller* instance) { FURI_LOG_D(TAG, "Auth External"); uint8_t blocks[2]; instance->data->data.fs.state.data[0] = 1; FelicaAuthentication* auth = &instance->auth; felica_calculate_mac_write( &auth->des_context, auth->session_key.data, instance->data->data.fs.rc.data, instance->data->data.fs.wcnt.data, instance->data->data.fs.state.data, instance->data->data.fs.mac_a.data); memcpy(instance->data->data.fs.mac_a.data + 8, instance->data->data.fs.wcnt.data, 3); //-V1086 uint8_t tx_data[FELICA_DATA_BLOCK_SIZE * 2]; memcpy(tx_data, instance->data->data.fs.state.data, FELICA_DATA_BLOCK_SIZE); memcpy( tx_data + FELICA_DATA_BLOCK_SIZE, instance->data->data.fs.mac_a.data, FELICA_DATA_BLOCK_SIZE); do { blocks[0] = FELICA_BLOCK_INDEX_STATE; blocks[1] = FELICA_BLOCK_INDEX_MAC_A; FelicaPollerWriteCommandResponse* tx_resp; FelicaError error = felica_poller_write_blocks(instance, 2, blocks, tx_data, &tx_resp); if(error != FelicaErrorNone || tx_resp->SF1 != 0 || tx_resp->SF2 != 0) break; FelicaPollerReadCommandResponse* rx_resp; error = felica_poller_read_blocks(instance, 1, blocks, FELICA_SERVICE_RO_ACCESS, &rx_resp); if(error != FelicaErrorNone || rx_resp->SF1 != 0 || rx_resp->SF2 != 0) break; instance->data->data.fs.state.SF1 = 0; instance->data->data.fs.state.SF2 = 0; memcpy(instance->data->data.fs.state.data, rx_resp->data, FELICA_DATA_BLOCK_SIZE); instance->auth.context.auth_status.external = instance->data->data.fs.state.data[0]; } while(false); switch(instance->data->workflow_type) { case FelicaStandard: instance->state = FelicaPollerStateTraverseStandardSystem; break; case FelicaLite: instance->state = FelicaPollerStateReadLiteBlocks; break; default: // Unimplemented instance->state = FelicaPollerStateReadSuccess; break; } return NfcCommandContinue; } NfcCommand felica_poller_state_handler_traverse_standard_system(FelicaPoller* instance) { FURI_LOG_D(TAG, "Traverse Standard System"); FelicaListServiceCommandResponse* response; felica_service_array_t service_buffer; felica_service_array_init(service_buffer); felica_area_array_t area_buffer; felica_area_array_init(area_buffer); for(uint16_t cursor = 0; cursor < 0xFFFF; cursor++) { FelicaError error = felica_poller_list_service_by_cursor(instance, cursor, &response); if(error != FelicaErrorNone) { FURI_LOG_E(TAG, "Error %d at cursor %04X", error, cursor); break; } uint8_t len = response->header.length; const uint8_t* list_service_payload = response->data; uint16_t code_begin = (uint16_t)(list_service_payload[0] | (list_service_payload[1] << 8)); if(len != 0x0C && len != 0x0E) { FURI_LOG_E(TAG, "Bad command resp length 0x%02X at cursor 0x%04X", len, cursor); break; } if(code_begin == 0xFFFF) { FURI_LOG_D(TAG, "Traverse complete"); break; } if(len == 0x0E) { FelicaArea* area = felica_area_array_push_raw(area_buffer); memset(area, 0, sizeof *area); area->code = code_begin; area->first_idx = (uint16_t)felica_service_array_size(service_buffer); area->last_idx = 0; } else { FelicaService* service = felica_service_array_push_raw(service_buffer); memset(service, 0, sizeof *service); service->code = code_begin; service->attr = (uint8_t)(code_begin & 0x3F); FURI_LOG_D(TAG, "Service %04X", service->code); if(felica_area_array_size(area_buffer)) { FelicaArea* current_area = felica_area_array_back(area_buffer); current_area->last_idx = (uint16_t)(felica_service_array_size(service_buffer) - 1); } } } const size_t service_num = felica_service_array_size(service_buffer); const size_t area_num = felica_area_array_size(area_buffer); FelicaSystem* system = simple_array_get(instance->data->systems, instance->systems_read); if(service_num) { simple_array_init(system->services, (uint32_t)service_num); memcpy( simple_array_get(system->services, 0), service_buffer->ptr, service_num * sizeof(FelicaService)); } else { simple_array_reset(system->services); } if(area_num) { simple_array_init(system->areas, (uint32_t)area_num); memcpy( simple_array_get(system->areas, 0), area_buffer->ptr, area_num * sizeof(FelicaArea)); } else { simple_array_reset(system->areas); } FURI_LOG_I( TAG, "Services found: %lu, Areas found: %lu", simple_array_get_count(system->services), simple_array_get_count(system->areas)); felica_service_array_clear(service_buffer); felica_area_array_clear(area_buffer); instance->state = FelicaPollerStateReadStandardBlocks; return NfcCommandContinue; } NfcCommand felica_poller_state_handler_read_standard_blocks(FelicaPoller* instance) { FURI_LOG_D(TAG, "Read Standard Blocks"); FelicaSystem* system = simple_array_get(instance->data->systems, instance->systems_read); const uint32_t service_count = simple_array_get_count(system->services); felica_public_block_array_t public_block_buffer; felica_public_block_array_init(public_block_buffer); bool have_read_anything = false; FelicaError error = FelicaErrorNone; for(uint32_t i = 0; i < service_count; i++) { const FelicaService* service = simple_array_get(system->services, i); if((service->attr & FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ) == 0) continue; uint8_t block_count = 1; uint8_t block_list[1] = {0}; FelicaPollerReadCommandResponse* response; do { error = felica_poller_read_blocks( instance, block_count, block_list, service->code, &response); if(error != FelicaErrorNone) { break; } if(response->SF1 == 0 && response->SF2 == 0) { FelicaPublicBlock* public_block = felica_public_block_array_push_raw(public_block_buffer); memset(public_block, 0, sizeof *public_block); memcpy(public_block->block.data, response->data, FELICA_DATA_BLOCK_SIZE); public_block->service_code = service->code; public_block->block_idx = block_list[0]; have_read_anything = true; block_list[0]++; } else { break; // No more blocks to read in this service, ok to continue for loop } } while(block_list[0] < FELICA_STANDARD_MAX_BLOCK_COUNT); if(error != FelicaErrorNone) { instance->felica_event.type = FelicaPollerEventTypeError; instance->felica_event_data.error = error; instance->state = FelicaPollerStateReadFailed; break; } } if(error == FelicaErrorNone) { instance->systems_read++; if(instance->systems_read == instance->systems_total) { instance->state = FelicaPollerStateReadSuccess; } else { instance->state = FelicaPollerStateSelectSystemIndex; } } if(have_read_anything) { const size_t n = felica_public_block_array_size(public_block_buffer); simple_array_init(system->public_blocks, (uint32_t)n); memcpy( simple_array_get(system->public_blocks, 0), public_block_buffer->ptr, n * sizeof(FelicaPublicBlock)); } felica_public_block_array_clear(public_block_buffer); return NfcCommandContinue; } NfcCommand felica_poller_state_handler_read_lite_blocks(FelicaPoller* instance) { FURI_LOG_D(TAG, "Read Lite Blocks"); uint8_t block_count = 1; uint8_t block_list[4] = {0, 0, 0, 0}; block_list[0] = instance->block_index; instance->block_index++; if(instance->block_index == FELICA_BLOCK_INDEX_REG + 1) { instance->block_index = FELICA_BLOCK_INDEX_RC; } else if(instance->block_index == FELICA_BLOCK_INDEX_MC + 1) { instance->block_index = FELICA_BLOCK_INDEX_WCNT; } else if(instance->block_index == FELICA_BLOCK_INDEX_STATE + 1) { instance->block_index = FELICA_BLOCK_INDEX_CRC_CHECK; } FelicaPollerReadCommandResponse* response; FelicaError error = felica_poller_read_blocks( instance, block_count, block_list, FELICA_SERVICE_RO_ACCESS, &response); if(error == FelicaErrorNone) { block_count = (response->SF1 == 0) ? response->block_count : block_count; uint8_t* data_ptr = instance->data->data.dump + instance->data->blocks_total * sizeof(FelicaBlock); *data_ptr++ = response->SF1; *data_ptr++ = response->SF2; if(response->SF1 == 0) { uint8_t* response_data_ptr = response->data; instance->data->blocks_read++; memcpy(data_ptr, response_data_ptr, FELICA_DATA_BLOCK_SIZE); } else { memset(data_ptr, 0, FELICA_DATA_BLOCK_SIZE); } instance->data->blocks_total++; if(instance->data->blocks_total == FELICA_BLOCKS_TOTAL_COUNT) { instance->state = FelicaPollerStateReadSuccess; } } else { instance->felica_event.type = FelicaPollerEventTypeError; instance->felica_event_data.error = error; instance->state = FelicaPollerStateReadFailed; } return NfcCommandContinue; } NfcCommand felica_poller_state_handler_read_success(FelicaPoller* instance) { FURI_LOG_D(TAG, "Read Success"); if(!instance->auth.context.auth_status.internal || !instance->auth.context.auth_status.external) { instance->data->blocks_read--; instance->felica_event.type = FelicaPollerEventTypeIncomplete; } else { memcpy( instance->data->data.fs.ck.data, instance->auth.context.card_key.data, FELICA_DATA_BLOCK_SIZE); instance->felica_event.type = FelicaPollerEventTypeReady; } instance->data->idm.data[0] &= 0x0F; instance->felica_event_data.error = FelicaErrorNone; return instance->callback(instance->general_event, instance->context); } NfcCommand felica_poller_state_handler_read_failed(FelicaPoller* instance) { FURI_LOG_D(TAG, "Read Fail"); instance->callback(instance->general_event, instance->context); instance->data->idm.data[0] &= 0x0F; return NfcCommandStop; } static const FelicaPollerReadHandler felica_poller_handler[FelicaPollerStateNum] = { [FelicaPollerStateIdle] = felica_poller_state_handler_idle, [FelicaPollerStateActivated] = felica_poller_state_handler_activate, [FelicaPollerStateListSystem] = felica_poller_state_handler_list_system, [FelicaPollerStateSelectSystemIndex] = felica_poller_state_handler_select_system_idx, [FelicaPollerStateAuthenticateInternal] = felica_poller_state_handler_auth_internal, [FelicaPollerStateAuthenticateExternal] = felica_poller_state_handler_auth_external, [FelicaPollerStateTraverseStandardSystem] = felica_poller_state_handler_traverse_standard_system, [FelicaPollerStateReadStandardBlocks] = felica_poller_state_handler_read_standard_blocks, [FelicaPollerStateReadLiteBlocks] = felica_poller_state_handler_read_lite_blocks, [FelicaPollerStateReadSuccess] = felica_poller_state_handler_read_success, [FelicaPollerStateReadFailed] = felica_poller_state_handler_read_failed, }; static NfcCommand felica_poller_run(NfcGenericEvent event, void* context) { furi_assert(context); furi_assert(event.protocol == NfcProtocolInvalid); furi_assert(event.event_data); FelicaPoller* instance = context; NfcEvent* nfc_event = event.event_data; NfcCommand command = NfcCommandContinue; if(nfc_event->type == NfcEventTypePollerReady) { command = felica_poller_handler[instance->state](instance); } return command; } static bool felica_poller_detect(NfcGenericEvent event, void* context) { furi_assert(context); furi_assert(event.event_data); furi_assert(event.instance); furi_assert(event.protocol == NfcProtocolInvalid); bool protocol_detected = false; FelicaPoller* instance = context; NfcEvent* nfc_event = event.event_data; furi_assert(instance->state == FelicaPollerStateIdle); if(nfc_event->type == NfcEventTypePollerReady) { FelicaError error = felica_poller_activate(instance, instance->data); protocol_detected = (error == FelicaErrorNone); } return protocol_detected; } const NfcPollerBase nfc_poller_felica = { .alloc = (NfcPollerAlloc)felica_poller_alloc, .free = (NfcPollerFree)felica_poller_free, .set_callback = (NfcPollerSetCallback)felica_poller_set_callback, .run = (NfcPollerRun)felica_poller_run, .detect = (NfcPollerDetect)felica_poller_detect, .get_data = (NfcPollerGetData)felica_poller_get_data, };