#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../minunit.h" #define TAG "NfcTest" #define NFC_TEST_NFC_DEV_PATH EXT_PATH("unit_tests/nfc/nfc_device_test.nfc") #define NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH EXT_PATH("unit_tests/mf_dict.nfc") typedef struct { Storage* storage; } NfcTest; static NfcTest* nfc_test = NULL; static void nfc_test_alloc() { nfc_test = malloc(sizeof(NfcTest)); nfc_test->storage = furi_record_open(RECORD_STORAGE); } static void nfc_test_free() { furi_check(nfc_test); furi_record_close(RECORD_STORAGE); free(nfc_test); nfc_test = NULL; } static void nfc_test_save_and_load(NfcDevice* nfc_device_ref) { NfcDevice* nfc_device_dut = nfc_device_alloc(); mu_assert( nfc_device_save(nfc_device_ref, NFC_TEST_NFC_DEV_PATH), "nfc_device_save() failed\r\n"); mu_assert( nfc_device_load(nfc_device_dut, NFC_TEST_NFC_DEV_PATH), "nfc_device_load() failed\r\n"); mu_assert( nfc_device_is_equal(nfc_device_ref, nfc_device_dut), "nfc_device_data_dut != nfc_device_data_ref\r\n"); mu_assert( storage_simply_remove(nfc_test->storage, NFC_TEST_NFC_DEV_PATH), "storage_simply_remove() failed\r\n"); nfc_device_free(nfc_device_dut); } static void iso14443_3a_file_test(uint8_t uid_len) { NfcDevice* nfc_device = nfc_device_alloc(); Iso14443_3aData* data = iso14443_3a_alloc(); data->uid_len = uid_len; furi_hal_random_fill_buf(data->uid, uid_len); furi_hal_random_fill_buf(data->atqa, sizeof(data->atqa)); furi_hal_random_fill_buf(&data->sak, 1); nfc_device_set_data(nfc_device, NfcProtocolIso14443_3a, data); nfc_test_save_and_load(nfc_device); iso14443_3a_free(data); nfc_device_free(nfc_device); } static void nfc_file_test_with_generator(NfcDataGeneratorType type) { NfcDevice* nfc_device_ref = nfc_device_alloc(); nfc_data_generator_fill_data(type, nfc_device_ref); nfc_test_save_and_load(nfc_device_ref); nfc_device_free(nfc_device_ref); } MU_TEST(iso14443_3a_4b_file_test) { iso14443_3a_file_test(4); } MU_TEST(iso14443_3a_7b_file_test) { iso14443_3a_file_test(7); } MU_TEST(mf_ultralight_file_test) { nfc_file_test_with_generator(NfcDataGeneratorTypeMfUltralight); } MU_TEST(mf_ultralight_ev1_11_file_test) { nfc_file_test_with_generator(NfcDataGeneratorTypeMfUltralightEV1_11); } MU_TEST(mf_ultralight_ev1_h11_file_test) { nfc_file_test_with_generator(NfcDataGeneratorTypeMfUltralightEV1_H11); } MU_TEST(mf_ultralight_ev1_21_file_test) { nfc_file_test_with_generator(NfcDataGeneratorTypeMfUltralightEV1_21); } MU_TEST(mf_ultralight_ev1_h21_file_test) { nfc_file_test_with_generator(NfcDataGeneratorTypeMfUltralightEV1_H21); } MU_TEST(mf_ultralight_ntag_203_file_test) { nfc_file_test_with_generator(NfcDataGeneratorTypeNTAG203); } MU_TEST(mf_ultralight_ntag_213_file_test) { nfc_file_test_with_generator(NfcDataGeneratorTypeNTAG213); } MU_TEST(mf_ultralight_ntag_215_file_test) { nfc_file_test_with_generator(NfcDataGeneratorTypeNTAG215); } MU_TEST(mf_ultralight_ntag_216_file_test) { nfc_file_test_with_generator(NfcDataGeneratorTypeNTAG216); } MU_TEST(mf_ultralight_ntag_i2c_1k_file_test) { nfc_file_test_with_generator(NfcDataGeneratorTypeNTAGI2C1k); } MU_TEST(mf_ultralight_ntag_i2c_2k_file_test) { nfc_file_test_with_generator(NfcDataGeneratorTypeNTAGI2C2k); } MU_TEST(mf_ultralight_ntag_i2c_plus_1k_file_test) { nfc_file_test_with_generator(NfcDataGeneratorTypeNTAGI2CPlus1k); } MU_TEST(mf_ultralight_ntag_i2c_plus_2k_file_test) { nfc_file_test_with_generator(NfcDataGeneratorTypeNTAGI2CPlus2k); } MU_TEST(mf_classic_mini_file_test) { nfc_file_test_with_generator(NfcDataGeneratorTypeMfClassicMini); } MU_TEST(mf_classic_1k_4b_file_test) { nfc_file_test_with_generator(NfcDataGeneratorTypeMfClassic1k_4b); } MU_TEST(mf_classic_1k_7b_file_test) { nfc_file_test_with_generator(NfcDataGeneratorTypeMfClassic1k_7b); } MU_TEST(mf_classic_4k_4b_file_test) { nfc_file_test_with_generator(NfcDataGeneratorTypeMfClassic4k_4b); } MU_TEST(mf_classic_4k_7b_file_test) { nfc_file_test_with_generator(NfcDataGeneratorTypeMfClassic4k_7b); } MU_TEST(iso14443_3a_reader) { Nfc* poller = nfc_alloc(); Nfc* listener = nfc_alloc(); Iso14443_3aData iso14443_3a_listener_data = { .uid_len = 7, .uid = {0x04, 0x51, 0x5C, 0xFA, 0x6F, 0x73, 0x81}, .atqa = {0x44, 0x00}, .sak = 0x00, }; NfcListener* iso3_listener = nfc_listener_alloc(listener, NfcProtocolIso14443_3a, &iso14443_3a_listener_data); nfc_listener_start(iso3_listener, NULL, NULL); Iso14443_3aData iso14443_3a_poller_data = {}; mu_assert( iso14443_3a_poller_sync_read(poller, &iso14443_3a_poller_data) == Iso14443_3aErrorNone, "iso14443_3a_poller_sync_read() failed"); nfc_listener_stop(iso3_listener); mu_assert( iso14443_3a_is_equal(&iso14443_3a_poller_data, &iso14443_3a_listener_data), "Data not matches"); nfc_listener_free(iso3_listener); nfc_free(listener); nfc_free(poller); } static void mf_ultralight_reader_test(const char* path) { FURI_LOG_I(TAG, "Testing file: %s", path); Nfc* poller = nfc_alloc(); Nfc* listener = nfc_alloc(); NfcDevice* nfc_device = nfc_device_alloc(); mu_assert(nfc_device_load(nfc_device, path), "nfc_device_load() failed\r\n"); MfUltralightData* data = (MfUltralightData*)nfc_device_get_data(nfc_device, NfcProtocolMfUltralight); uint32_t features = mf_ultralight_get_feature_support_set(data->type); bool pwd_supported = mf_ultralight_support_feature(features, MfUltralightFeatureSupportPasswordAuth); uint8_t pwd_num = mf_ultralight_get_pwd_page_num(data->type); const uint8_t zero_pwd[4] = {0, 0, 0, 0}; if(pwd_supported && !memcmp(data->page[pwd_num].data, zero_pwd, sizeof(zero_pwd))) { data->pages_read -= 2; } NfcListener* mfu_listener = nfc_listener_alloc(listener, NfcProtocolMfUltralight, data); nfc_listener_start(mfu_listener, NULL, NULL); MfUltralightData* mfu_data = mf_ultralight_alloc(); MfUltralightError error = mf_ultralight_poller_sync_read_card(poller, mfu_data); mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_sync_read_card() failed"); nfc_listener_stop(mfu_listener); nfc_listener_free(mfu_listener); mu_assert( mf_ultralight_is_equal(mfu_data, nfc_device_get_data(nfc_device, NfcProtocolMfUltralight)), "Data not matches"); mf_ultralight_free(mfu_data); nfc_device_free(nfc_device); nfc_free(listener); nfc_free(poller); } MU_TEST(mf_ultralight_11_reader) { mf_ultralight_reader_test(EXT_PATH("unit_tests/nfc/Ultralight_11.nfc")); } MU_TEST(mf_ultralight_21_reader) { mf_ultralight_reader_test(EXT_PATH("unit_tests/nfc/Ultralight_21.nfc")); } MU_TEST(ntag_215_reader) { mf_ultralight_reader_test(EXT_PATH("unit_tests/nfc/Ntag215.nfc")); } MU_TEST(ntag_216_reader) { mf_ultralight_reader_test(EXT_PATH("unit_tests/nfc/Ntag216.nfc")); } MU_TEST(ntag_213_locked_reader) { FURI_LOG_I(TAG, "Testing Ntag215 locked file"); Nfc* poller = nfc_alloc(); Nfc* listener = nfc_alloc(); NfcDeviceData* nfc_device = nfc_device_alloc(); mu_assert( nfc_device_load(nfc_device, EXT_PATH("unit_tests/nfc/Ntag213_locked.nfc")), "nfc_device_load() failed\r\n"); NfcListener* mfu_listener = nfc_listener_alloc( listener, NfcProtocolMfUltralight, nfc_device_get_data(nfc_device, NfcProtocolMfUltralight)); nfc_listener_start(mfu_listener, NULL, NULL); MfUltralightData* mfu_data = mf_ultralight_alloc(); MfUltralightError error = mf_ultralight_poller_sync_read_card(poller, mfu_data); mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_sync_read_card() failed"); nfc_listener_stop(mfu_listener); nfc_listener_free(mfu_listener); MfUltralightConfigPages* config = NULL; const MfUltralightData* mfu_ref_data = nfc_device_get_data(nfc_device, NfcProtocolMfUltralight); mu_assert( mf_ultralight_get_config_page(mfu_ref_data, &config), "mf_ultralight_get_config_page() failed"); uint16_t pages_locked = config->auth0; mu_assert(mfu_data->pages_read == pages_locked, "Unexpected pages read"); mf_ultralight_free(mfu_data); nfc_device_free(nfc_device); nfc_free(listener); nfc_free(poller); } static void mf_ultralight_write() { Nfc* poller = nfc_alloc(); Nfc* listener = nfc_alloc(); NfcDevice* nfc_device = nfc_device_alloc(); nfc_data_generator_fill_data(NfcDataGeneratorTypeMfUltralightEV1_21, nfc_device); NfcListener* mfu_listener = nfc_listener_alloc( listener, NfcProtocolMfUltralight, nfc_device_get_data(nfc_device, NfcProtocolMfUltralight)); nfc_listener_start(mfu_listener, NULL, NULL); MfUltralightData* mfu_data = mf_ultralight_alloc(); // Initial read MfUltralightError error = mf_ultralight_poller_sync_read_card(poller, mfu_data); mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_sync_read_card() failed"); mu_assert( mf_ultralight_is_equal(mfu_data, nfc_device_get_data(nfc_device, NfcProtocolMfUltralight)), "Data not matches"); // Write random data for(size_t i = 5; i < 15; i++) { MfUltralightPage page = {}; FURI_LOG_D(TAG, "Writing page %d", i); furi_hal_random_fill_buf(page.data, sizeof(MfUltralightPage)); mfu_data->page[i] = page; error = mf_ultralight_poller_sync_write_page(poller, i, &page); mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_sync_write_page() failed"); } // Verification read error = mf_ultralight_poller_sync_read_card(poller, mfu_data); mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_sync_read_card() failed"); nfc_listener_stop(mfu_listener); const MfUltralightData* mfu_listener_data = nfc_listener_get_data(mfu_listener, NfcProtocolMfUltralight); mu_assert(mf_ultralight_is_equal(mfu_data, mfu_listener_data), "Data not matches"); nfc_listener_free(mfu_listener); mf_ultralight_free(mfu_data); nfc_device_free(nfc_device); nfc_free(listener); nfc_free(poller); } static void mf_classic_reader() { Nfc* poller = nfc_alloc(); Nfc* listener = nfc_alloc(); NfcDevice* nfc_device = nfc_device_alloc(); nfc_data_generator_fill_data(NfcDataGeneratorTypeMfClassic4k_7b, nfc_device); NfcListener* mfc_listener = nfc_listener_alloc( listener, NfcProtocolMfClassic, nfc_device_get_data(nfc_device, NfcProtocolMfClassic)); nfc_listener_start(mfc_listener, NULL, NULL); MfClassicBlock block = {}; MfClassicKey key = {.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}; mf_classic_poller_sync_read_block(poller, 0, &key, MfClassicKeyTypeA, &block); nfc_listener_stop(mfc_listener); nfc_listener_free(mfc_listener); const MfClassicData* mfc_data = nfc_device_get_data(nfc_device, NfcProtocolMfClassic); mu_assert(memcmp(&mfc_data->block[0], &block, sizeof(MfClassicBlock)) == 0, "Data mismatch"); nfc_device_free(nfc_device); nfc_free(listener); nfc_free(poller); } static void mf_classic_write() { Nfc* poller = nfc_alloc(); Nfc* listener = nfc_alloc(); NfcDevice* nfc_device = nfc_device_alloc(); nfc_data_generator_fill_data(NfcDataGeneratorTypeMfClassic4k_7b, nfc_device); NfcListener* mfc_listener = nfc_listener_alloc( listener, NfcProtocolMfClassic, nfc_device_get_data(nfc_device, NfcProtocolMfClassic)); nfc_listener_start(mfc_listener, NULL, NULL); MfClassicBlock block_write = {}; MfClassicBlock block_read = {}; furi_hal_random_fill_buf(block_write.data, sizeof(MfClassicBlock)); MfClassicKey key = {.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}; mf_classic_poller_sync_write_block(poller, 1, &key, MfClassicKeyTypeA, &block_write); mf_classic_poller_sync_read_block(poller, 1, &key, MfClassicKeyTypeA, &block_read); nfc_listener_stop(mfc_listener); nfc_listener_free(mfc_listener); mu_assert(memcmp(&block_read, &block_write, sizeof(MfClassicBlock)) == 0, "Data mismatch"); nfc_device_free(nfc_device); nfc_free(listener); nfc_free(poller); } static void mf_classic_value_block() { Nfc* poller = nfc_alloc(); Nfc* listener = nfc_alloc(); NfcDevice* nfc_device = nfc_device_alloc(); nfc_data_generator_fill_data(NfcDataGeneratorTypeMfClassic4k_7b, nfc_device); NfcListener* mfc_listener = nfc_listener_alloc( listener, NfcProtocolMfClassic, nfc_device_get_data(nfc_device, NfcProtocolMfClassic)); nfc_listener_start(mfc_listener, NULL, NULL); MfClassicKey key = {.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}; int32_t value = 228; MfClassicBlock block_write = {}; mf_classic_value_to_block(value, 1, &block_write); MfClassicError error = MfClassicErrorNone; error = mf_classic_poller_sync_write_block(poller, 1, &key, MfClassicKeyTypeA, &block_write); mu_assert(error == MfClassicErrorNone, "Write failed"); int32_t data = 200; int32_t new_value = 0; error = mf_classic_poller_sync_change_value(poller, 1, &key, MfClassicKeyTypeA, data, &new_value); mu_assert(error == MfClassicErrorNone, "Value increment failed"); mu_assert(new_value == value + data, "Value not match"); error = mf_classic_poller_sync_change_value(poller, 1, &key, MfClassicKeyTypeA, -data, &new_value); mu_assert(error == MfClassicErrorNone, "Value decrement failed"); mu_assert(new_value == value, "Value not match"); nfc_listener_stop(mfc_listener); nfc_listener_free(mfc_listener); nfc_device_free(nfc_device); nfc_free(listener); nfc_free(poller); } MU_TEST(mf_classic_dict_test) { Storage* storage = furi_record_open(RECORD_STORAGE); if(storage_common_stat(storage, NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH, NULL) == FSE_OK) { mu_assert( storage_simply_remove(storage, NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH), "Remove test dict failed"); } KeysDict* dict = keys_dict_alloc( NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey)); mu_assert(dict != NULL, "keys_dict_alloc() failed"); size_t dict_keys_total = keys_dict_get_total_keys(dict); mu_assert(dict_keys_total == 0, "keys_dict_keys_total() failed"); const uint32_t test_key_num = 30; MfClassicKey* key_arr_ref = malloc(test_key_num * sizeof(MfClassicKey)); for(size_t i = 0; i < test_key_num; i++) { furi_hal_random_fill_buf(key_arr_ref[i].data, sizeof(MfClassicKey)); mu_assert( keys_dict_add_key(dict, key_arr_ref[i].data, sizeof(MfClassicKey)), "add key failed"); size_t dict_keys_total = keys_dict_get_total_keys(dict); mu_assert(dict_keys_total == (i + 1), "keys_dict_keys_total() failed"); } keys_dict_free(dict); dict = keys_dict_alloc( NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey)); mu_assert(dict != NULL, "keys_dict_alloc() failed"); dict_keys_total = keys_dict_get_total_keys(dict); mu_assert(dict_keys_total == test_key_num, "keys_dict_keys_total() failed"); MfClassicKey key_dut = {}; size_t key_idx = 0; while(keys_dict_get_next_key(dict, key_dut.data, sizeof(MfClassicKey))) { mu_assert( memcmp(key_arr_ref[key_idx].data, key_dut.data, sizeof(MfClassicKey)) == 0, "Loaded key data mismatch"); key_idx++; } uint32_t delete_keys_idx[] = {1, 3, 9, 11, 19, 27}; for(size_t i = 0; i < COUNT_OF(delete_keys_idx); i++) { MfClassicKey* key = &key_arr_ref[delete_keys_idx[i]]; mu_assert( keys_dict_is_key_present(dict, key->data, sizeof(MfClassicKey)), "keys_dict_is_key_present() failed"); mu_assert( keys_dict_delete_key(dict, key->data, sizeof(MfClassicKey)), "keys_dict_delete_key() failed"); } dict_keys_total = keys_dict_get_total_keys(dict); mu_assert( dict_keys_total == test_key_num - COUNT_OF(delete_keys_idx), "keys_dict_keys_total() failed"); keys_dict_free(dict); free(key_arr_ref); mu_assert( storage_simply_remove(storage, NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH), "Remove test dict failed"); } MU_TEST_SUITE(nfc) { nfc_test_alloc(); MU_RUN_TEST(iso14443_3a_reader); MU_RUN_TEST(mf_ultralight_11_reader); MU_RUN_TEST(mf_ultralight_21_reader); MU_RUN_TEST(ntag_215_reader); MU_RUN_TEST(ntag_216_reader); MU_RUN_TEST(ntag_213_locked_reader); MU_RUN_TEST(mf_ultralight_write); MU_RUN_TEST(iso14443_3a_4b_file_test); MU_RUN_TEST(iso14443_3a_7b_file_test); MU_RUN_TEST(mf_ultralight_file_test); MU_RUN_TEST(mf_ultralight_ev1_11_file_test); MU_RUN_TEST(mf_ultralight_ev1_h11_file_test); MU_RUN_TEST(mf_ultralight_ev1_21_file_test); MU_RUN_TEST(mf_ultralight_ev1_h21_file_test); MU_RUN_TEST(mf_ultralight_ntag_203_file_test); MU_RUN_TEST(mf_ultralight_ntag_213_file_test); MU_RUN_TEST(mf_ultralight_ntag_215_file_test); MU_RUN_TEST(mf_ultralight_ntag_216_file_test); MU_RUN_TEST(mf_ultralight_ntag_i2c_1k_file_test); MU_RUN_TEST(mf_ultralight_ntag_i2c_2k_file_test); MU_RUN_TEST(mf_ultralight_ntag_i2c_plus_1k_file_test); MU_RUN_TEST(mf_ultralight_ntag_i2c_plus_2k_file_test); MU_RUN_TEST(mf_classic_mini_file_test); MU_RUN_TEST(mf_classic_1k_4b_file_test); MU_RUN_TEST(mf_classic_1k_7b_file_test); MU_RUN_TEST(mf_classic_4k_4b_file_test); MU_RUN_TEST(mf_classic_4k_7b_file_test); MU_RUN_TEST(mf_classic_reader); MU_RUN_TEST(mf_classic_write); MU_RUN_TEST(mf_classic_value_block); MU_RUN_TEST(mf_classic_dict_test); nfc_test_free(); } int run_minunit_test_nfc() { MU_RUN_SUITE(nfc); return MU_EXIT_CODE; }