 7642d67cae
			
		
	
	
		7642d67cae
		
			
		
	
	
	
	
		
			
			* toolbox(keys_dict): generalize nfc_dict * nfc: rework nfc app and tests * toolbox(keys_dict): improve code readability
		
			
				
	
	
		
			555 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			555 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include <furi.h>
 | |
| #include <furi_hal.h>
 | |
| #include <storage/storage.h>
 | |
| 
 | |
| #include <nfc/nfc_device.h>
 | |
| #include <nfc/helpers/nfc_data_generator.h>
 | |
| #include <nfc/nfc_poller.h>
 | |
| #include <nfc/nfc_listener.h>
 | |
| #include <nfc/protocols/iso14443_3a/iso14443_3a.h>
 | |
| #include <nfc/protocols/iso14443_3a/iso14443_3a_poller_sync.h>
 | |
| #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 <toolbox/keys_dict.h>
 | |
| #include <nfc/nfc.h>
 | |
| 
 | |
| #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;
 | |
| }
 |