Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FL-3810] Felica emulation #3673

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
bb5ba4c
Moved some structs and defs from poller to
RebornedBrain Apr 18, 2024
11c551a
Buffer size increased for transferring more data
RebornedBrain Apr 18, 2024
1ddb5b0
Felica HAL Tx function implemented
RebornedBrain Apr 18, 2024
e364cc8
Some structs and fields for listener
RebornedBrain Apr 18, 2024
d403bc6
Raw listener implementation
RebornedBrain Apr 18, 2024
e3a423f
Added new event for felica activation
RebornedBrain Apr 26, 2024
eec512b
Proper config fot listener added
RebornedBrain Apr 26, 2024
b490d55
Moved some structs from poller in order to use them in listener too
RebornedBrain May 15, 2024
cd0f158
New function for calculating MAC
RebornedBrain May 15, 2024
90e111b
Listener data structures and function definitions
RebornedBrain May 15, 2024
fd43845
Private listener functions implementation added
RebornedBrain May 15, 2024
91d45bd
Raw felica listener logic implementation added
RebornedBrain May 15, 2024
7aa42be
Fix total sector count both for poller and listener
RebornedBrain May 15, 2024
63c046c
Defined type for write handlers
RebornedBrain May 17, 2024
80db705
New logic for write operations added
RebornedBrain May 17, 2024
c8e8e97
Removed old commented code
RebornedBrain May 17, 2024
9fce16e
Splitted read logic into several separate functions
RebornedBrain May 18, 2024
a5079d7
New type added and some fields to instance
RebornedBrain May 18, 2024
0f1d980
New logic of read command implemented
RebornedBrain May 18, 2024
a8252e2
Defines added for response codes
RebornedBrain May 20, 2024
0c3c6f3
Functions moved to private namespace
RebornedBrain May 20, 2024
ca08c1e
Function visibility changed and some cleanups
RebornedBrain May 20, 2024
5a106ac
Update felica_listener.c, felica_listener_i.c, and felica_listener_i.h
RebornedBrain May 20, 2024
3e3057b
Some type adjustments
RebornedBrain May 20, 2024
3ad452c
Moved frame_exchange function to private namespace
RebornedBrain May 20, 2024
986f848
Error handling added
RebornedBrain May 20, 2024
e7889db
Function to get data_ptr for write request added
RebornedBrain May 20, 2024
5a7f589
Missing declaration added
RebornedBrain May 20, 2024
52d5ff9
Add processing of nfc errors
RebornedBrain May 20, 2024
38fcc77
write_with_mac is a local variable now
RebornedBrain May 20, 2024
49d14f2
Adjustments to MAC calculation logic
RebornedBrain May 20, 2024
6ee1456
Values replaced with defines
RebornedBrain May 20, 2024
289f78b
Update nfc_transport.c with felica logic
RebornedBrain May 22, 2024
4d1accb
Sync felica poller added for unit tests
RebornedBrain May 22, 2024
39d13f5
Felica unit_tests and data dump added
RebornedBrain May 23, 2024
e0886c1
Fixed proper reading of MAC_A block when it is 1st
RebornedBrain May 23, 2024
3b5aa06
Macro definitions for MC added
RebornedBrain May 23, 2024
b22c6d7
Function simplified
RebornedBrain May 23, 2024
d1436dc
More defines
RebornedBrain May 24, 2024
42e1478
CRC check for incomming packets added
RebornedBrain May 24, 2024
cd88d64
Readonly logic adjusted
RebornedBrain May 24, 2024
9bb8459
Block write validation adjusted
RebornedBrain May 24, 2024
f3ac142
New logic for ID block writing
RebornedBrain May 24, 2024
2f9d061
Some cleanups
RebornedBrain May 24, 2024
6864c78
New logic of moving across the block list with different element length
RebornedBrain May 27, 2024
41711d0
Some cleanups
RebornedBrain May 27, 2024
1b83e8b
Adjusted requires_mac logic to cover all blocks needed
RebornedBrain May 28, 2024
e5900a7
Cleanups and renaming
RebornedBrain May 28, 2024
576e6fd
New block list validation logic
RebornedBrain May 29, 2024
21c649b
Block list logic iteration simplified
RebornedBrain May 30, 2024
24bf13f
Some asserts and checks added
RebornedBrain May 30, 2024
a0b5181
Replaced MC[2] checks with macros
RebornedBrain May 30, 2024
bea5bcc
Marked def values as unsigned
RebornedBrain May 30, 2024
9ce41bb
Removed old code
RebornedBrain May 30, 2024
51eda45
Removed commented function declarations
RebornedBrain May 30, 2024
0f64d9c
Changed protected block in felica test card dump and adjusted tests
RebornedBrain May 30, 2024
d2a8bd4
Merge branch 'dev'
RebornedBrain May 30, 2024
b64ca25
Merge branch 'dev'
RebornedBrain May 30, 2024
84beb72
Fixes after merge
RebornedBrain May 30, 2024
59b85c1
Moved defines to header
RebornedBrain May 31, 2024
c946bb7
Now we allocate memory for max possible response pack in any case
RebornedBrain May 31, 2024
68d2b5e
Some renaming and documentation
RebornedBrain May 31, 2024
06c7e18
Merge remote-tracking branch 'origin/dev' into reborned/felica_listen…
skotopes Jun 3, 2024
e56b725
Bump api symbols
skotopes Jun 3, 2024
05e80a9
Set feature to emulate full for felica
RebornedBrain Jun 3, 2024
8cfa82f
Removed 'More' button and added MoreInfo feature which adds this butt…
RebornedBrain Jun 3, 2024
0982490
Types renamed
RebornedBrain Jun 4, 2024
11baefd
Removed unnecessary code
RebornedBrain Jun 4, 2024
2215b51
Reformat comments
RebornedBrain Jun 4, 2024
cc18378
Fixing missing signatures
RebornedBrain Jun 5, 2024
a50d229
Replaced crash with error log and return value
RebornedBrain Jun 5, 2024
f79b0a8
Merge remote-tracking branch 'origin/dev' into reborned/felica_listen…
skotopes Jun 8, 2024
8856ec5
Format doxygen comments
skotopes Jun 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
Filetype: Flipper NFC device
Version: 4
# Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, ISO14443-4B, ISO15693-3, FeliCa, NTAG/Ultralight, Mifare Classic, Mifare DESFire, SLIX, ST25TB
Device type: FeliCa
# UID is common for all formats
UID: 29 9F FA 53 AB 75 87 6E
# FeliCa specific data
Data format version: 1
Manufacture id: 29 9F FA 53 AB 75 87 6E
Manufacture parameter: 57 4E 10 2A 94 16 BC 8E
Blocks total: 28
Blocks read: 28
Block 0: 00 00 DE AD BE AF 00 00 00 00 00 00 00 00 DE AD BE AF
Block 1: 00 00 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
Block 2: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 3: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 4: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 5: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 6: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 7: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 8: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 9: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 11: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 12: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 13: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 14: 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
Block 15: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 16: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 17: 00 00 29 9F FA 53 AB 75 87 6E 57 4E 10 2A 94 16 BC 8E
Block 18: 00 00 29 9F FA 53 AB 75 87 6E 00 F1 00 00 00 01 43 00
Block 19: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 20: 00 00 88 B4 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 21: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 22: 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
Block 23: 00 00 FF FF FF 00 FF 00 10 00 00 00 00 00 00 00 00 00
Block 24: 00 00 24 FE FF 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 25: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 26: 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 27: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
54 changes: 54 additions & 0 deletions applications/debug/unit_tests/tests/nfc/nfc_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include <nfc/protocols/mf_ultralight/mf_ultralight.h>
#include <nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#include <nfc/protocols/felica/felica.h>
#include <nfc/protocols/felica/felica_poller_sync.h>
#include <nfc/protocols/mf_classic/mf_classic_poller.h>
#include <nfc/protocols/iso15693_3/iso15693_3_poller.h>
#include <nfc/protocols/slix/slix.h>
Expand Down Expand Up @@ -646,6 +648,56 @@ MU_TEST(mf_classic_dict_test) {
"Remove test dict failed");
}

static FelicaError
felica_do_request_response(FelicaData* felica_data, const FelicaCardKey* card_key) {
NfcDeviceData* nfc_device = nfc_device_alloc();

FelicaError error = FelicaErrorNone;
if(!nfc_device_load(nfc_device, EXT_PATH("unit_tests/nfc/Felica.nfc"))) {
error = FelicaErrorNotPresent;
} else {
Nfc* poller = nfc_alloc();
Nfc* listener = nfc_alloc();
NfcListener* felica_listener = nfc_listener_alloc(
listener, NfcProtocolFelica, nfc_device_get_data(nfc_device, NfcProtocolFelica));
nfc_listener_start(felica_listener, NULL, NULL);

error = felica_poller_sync_read(poller, felica_data, card_key);

nfc_listener_stop(felica_listener);
nfc_listener_free(felica_listener);

nfc_free(listener);
nfc_free(poller);
}

nfc_device_free(nfc_device);
return error;
}

MU_TEST(felica_read) {
FelicaData* felica_data = felica_alloc();
FelicaError error = felica_do_request_response(felica_data, NULL);
mu_assert(error == FelicaErrorNone, "felica_poller() failed");
mu_assert(felica_data->data.fs.spad[4].SF1 == 0x01, "block[4].SF1 != 0x01");
mu_assert(felica_data->data.fs.spad[4].SF2 == 0xB1, "block[4].SF2 != 0xB1");

felica_free(felica_data);
}

MU_TEST(felica_read_auth) {
FelicaData* felica_data = felica_alloc();
FelicaCardKey card_key;
memset(card_key.data, 0xFF, FELICA_DATA_BLOCK_SIZE);

FelicaError error = felica_do_request_response(felica_data, &card_key);
mu_assert(error == FelicaErrorNone, "felica_poller() failed");
mu_assert(felica_data->data.fs.spad[4].SF1 == 0x00, "block[4].SF1 != 0x00");
mu_assert(felica_data->data.fs.spad[4].SF2 == 0x00, "block[4].SF2 != 0x00");

felica_free(felica_data);
}

MU_TEST(slix_file_with_capabilities_test) {
NfcDevice* nfc_device_missed_cap = nfc_device_alloc();
mu_assert(
Expand Down Expand Up @@ -807,6 +859,8 @@ MU_TEST_SUITE(nfc) {
MU_RUN_TEST(mf_classic_value_block);
MU_RUN_TEST(mf_classic_send_frame_test);
MU_RUN_TEST(mf_classic_dict_test);
MU_RUN_TEST(felica_read);
MU_RUN_TEST(felica_read_auth);

MU_RUN_TEST(slix_file_with_capabilities_test);
MU_RUN_TEST(slix_set_password_default_cap_correct_pass);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,6 @@ static void nfc_scene_info_on_enter_felica(NfcApp* instance) {
widget_add_text_scroll_element(
instance->widget, 0, 0, 128, 48, furi_string_get_cstr(temp_str));

widget_add_button_element(
instance->widget,
GuiButtonTypeRight,
"More",
nfc_protocol_support_common_widget_callback,
instance);
furi_string_free(temp_str);
}

Expand Down Expand Up @@ -177,7 +171,7 @@ static bool nfc_scene_read_menu_on_event_felica(NfcApp* instance, SceneManagerEv
}

const NfcProtocolSupportBase nfc_protocol_support_felica = {
.features = NfcProtocolFeatureEmulateUid,
.features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo,

.scene_info =
{
Expand Down
2 changes: 2 additions & 0 deletions lib/nfc/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,13 @@ env.Append(
File("protocols/iso14443_4a/iso14443_4a_listener.h"),
File("protocols/mf_ultralight/mf_ultralight_listener.h"),
File("protocols/mf_classic/mf_classic_listener.h"),
File("protocols/felica/felica_listener.h"),
# Sync API
File("protocols/iso14443_3a/iso14443_3a_poller_sync.h"),
File("protocols/mf_ultralight/mf_ultralight_poller_sync.h"),
File("protocols/mf_classic/mf_classic_poller_sync.h"),
File("protocols/st25tb/st25tb_poller_sync.h"),
File("protocols/felica/felica_poller_sync.h"),
# Misc
File("helpers/nfc_util.h"),
File("helpers/iso14443_crc.h"),
Expand Down
5 changes: 5 additions & 0 deletions lib/nfc/helpers/felica_crc.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#define FELICA_CRC_INIT (0x0000U)

uint16_t felica_crc_calculate(const uint8_t* data, size_t length) {
furi_check(data);

uint16_t crc = FELICA_CRC_INIT;

for(size_t i = 0; i < length; i++) {
Expand All @@ -24,6 +26,7 @@ uint16_t felica_crc_calculate(const uint8_t* data, size_t length) {
}

void felica_crc_append(BitBuffer* buf) {
furi_check(buf);
const uint8_t* data = bit_buffer_get_data(buf);
const size_t data_size = bit_buffer_get_size_bytes(buf);

Expand All @@ -32,6 +35,7 @@ void felica_crc_append(BitBuffer* buf) {
}

bool felica_crc_check(const BitBuffer* buf) {
furi_check(buf);
const size_t data_size = bit_buffer_get_size_bytes(buf);
if(data_size <= FELICA_CRC_SIZE) return false;

Expand All @@ -45,6 +49,7 @@ bool felica_crc_check(const BitBuffer* buf) {
}

void felica_crc_trim(BitBuffer* buf) {
furi_check(buf);
const size_t data_size = bit_buffer_get_size_bytes(buf);
furi_assert(data_size > FELICA_CRC_SIZE);

Expand Down
44 changes: 44 additions & 0 deletions lib/nfc/nfc_mock.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
#include <lib/nfc/nfc.h>
#include <lib/nfc/helpers/iso14443_crc.h>
#include <lib/nfc/protocols/iso14443_3a/iso14443_3a.h>
#include <lib/nfc/protocols/felica/felica.h>
#include <lib/nfc/helpers/felica_crc.h>
#include <lib/nfc/protocols/felica/felica_poller_sync.h>

#include <furi/furi.h>

Expand Down Expand Up @@ -50,11 +53,31 @@ typedef struct {
Iso14443_3aSelResp sel_resp[2];
} Iso14443_3aColResData;

typedef struct {
uint8_t length;
uint8_t polling_cmd;
uint16_t system_code;
uint8_t request_code;
uint8_t time_slot;
} FelicaPollingRequest;

typedef struct {
uint8_t code;
FelicaIDm idm;
FelicaPMm pmm;
} FelicaSensfResData;

typedef struct {
uint16_t system_code;
FelicaSensfResData sens_res;
} FelicaPTMemory;

struct Nfc {
NfcState state;

Iso14443_3aColResStatus col_res_status;
Iso14443_3aColResData col_res_data;
FelicaPTMemory pt_memory;
bool software_col_res_required;

NfcEventCallback callback;
Expand Down Expand Up @@ -243,6 +266,21 @@ static void nfc_worker_listener_pass_col_res(Nfc* instance, uint8_t* rx_data, ui
NfcEvent event = {.type = NfcEventTypeListenerActivated};
instance->callback(event, instance->context);

processed = true;
}
} else if(rx_bits == 8 * 8) {
FelicaPollingRequest* request = (FelicaPollingRequest*)rx_data;
if(request->system_code == instance->pt_memory.system_code) {
uint8_t response_size = sizeof(FelicaSensfResData) + 1;
bit_buffer_reset(tx_buffer);
bit_buffer_append_byte(tx_buffer, response_size);
bit_buffer_append_bytes(
tx_buffer, (uint8_t*)&instance->pt_memory.sens_res, sizeof(FelicaSensfResData));
felica_crc_append(tx_buffer);
nfc_listener_tx(instance, tx_buffer);
instance->col_res_status = Iso14443_3aColResStatusDone;
NfcEvent event = {.type = NfcEventTypeListenerActivated};
instance->callback(event, instance->context);
processed = true;
}
}
Expand Down Expand Up @@ -470,6 +508,12 @@ NfcError nfc_felica_listener_set_sensf_res_data(
furi_assert(idm_len == 8);
furi_assert(pmm_len == 8);

instance->pt_memory.system_code = 0xFFFF;
instance->pt_memory.sens_res.code = 0x01;
instance->software_col_res_required = true;
memcpy(instance->pt_memory.sens_res.idm.data, idm, idm_len);
memcpy(instance->pt_memory.sens_res.pmm.data, pmm, pmm_len);

return NfcErrorNone;
}

Expand Down
28 changes: 23 additions & 5 deletions lib/nfc/protocols/felica/felica.c
Original file line number Diff line number Diff line change
Expand Up @@ -298,15 +298,33 @@ bool felica_check_mac(
furi_check(blocks);
furi_check(data);

uint8_t first_block[8];
uint8_t mac[8];
felica_prepare_first_block(FelicaMACTypeRead, blocks, block_count, first_block);
felica_calculate_mac_read(ctx, session_key, rc, blocks, block_count, data, mac);

uint8_t mac_offset = FELICA_DATA_BLOCK_SIZE * (block_count - 1);
uint8_t* mac_ptr = data + mac_offset;
return !memcmp(mac, mac_ptr, 8);
}

void felica_calculate_mac_read(
mbedtls_des3_context* ctx,
const uint8_t* session_key,
const uint8_t* rc,
const uint8_t* blocks,
const uint8_t block_count,
const uint8_t* data,
uint8_t* mac) {
furi_check(ctx);
furi_check(session_key);
furi_check(rc);
furi_check(blocks);
furi_check(data);
furi_check(mac);

uint8_t first_block[8];
felica_prepare_first_block(FelicaMACTypeRead, blocks, block_count, first_block);
uint8_t data_size_without_mac = FELICA_DATA_BLOCK_SIZE * (block_count - 1);
felica_calculate_mac(ctx, session_key, rc, first_block, data, data_size_without_mac, mac);

uint8_t* mac_ptr = data + data_size_without_mac;
return !memcmp(mac, mac_ptr, 8);
}

void felica_calculate_mac_write(
Expand Down
Loading